Resolve conflicts from upstream's Effect-service normalization batch while preserving the WSL parallel-backend feature set. Key resolutions: - ipc/methods/window.ts: adopt the DesktopIpc.makeIpcMethod / DesktopIpc.makeSyncIpcMethod namespace convention; keep our plural getLocalEnvironmentBootstraps and the getLocalEnvironmentBearerToken method. - app/DesktopState.ts: adopt upstream's Context.Service class with backendReady + quitting. The window keeps its own backendReadyRef, so state.backendReady is unused but harmless. - app/DesktopObservability.ts: keep our per-instance DesktopBackendOutputLogFactory (per-instance backend logs are required for parallel backends). Drop upstream's newly-extracted single-instance DesktopBackendOutputLog.ts: zero consumers in this tree, fully superseded by the factory (primary maps to forInstance(primary), same server-child.log path). - backend/DesktopBackendManager.test.ts: keep our factory-based harness. Upstream's side provisioned the removed DesktopBackendManager.layer service model (DesktopState / DesktopWindow / DesktopBackendConfiguration deps), which no longer exists in this tree. - Adapt DesktopObservability.ts + ipc/methods/wsl.test.ts to upstream's removal of the standalone DesktopEnvironmentShape export: use DesktopEnvironment.DesktopEnvironment["Service"] (the normalized form).
Unified · Split
| @@ -2,10 +2,12 @@ name: Mobile EAS Preview · 1 unmodified line | ||
| 2 | 2 | |
| 3 | 3 | on: |
| 4 | 4 | pull_request: |
| 5 | + types: [opened, reopened, synchronize, labeled, unlabeled] | |
| 5 | 6 | |
| 6 | 7 | jobs: |
| 7 | 8 | preview: |
| 8 | 9 | name: EAS Preview |
| 10 | + if: contains(github.event.pull_request.labels.*.name, '🚀 Mobile Continuous Deployment') | |
| 9 | 11 | runs-on: blacksmith-8vcpu-ubuntu-2404 |
| 10 | 12 | permissions: |
| 11 | 13 | contents: read |
| @@ -63,7 +63,7 @@ const makeElectronAppLayer = (calls: ElectronAppCalls) => · 62 unmodified lines | ||
| 63 | 63 | }), |
| 64 | 64 | appendCommandLineSwitch: () => Effect.void, |
| 65 | 65 | on: () => Effect.void, |
| 66 | − } satisfies ElectronApp.ElectronAppShape); | |
| 66 | + } satisfies ElectronApp.ElectronApp["Service"]); | |
| 67 | 67 | |
| 68 | 68 | const makeAssetsLayer = (png: Option.Option<string>) => |
| 69 | 69 | Layer.succeed(DesktopAssets.DesktopAssets, { |
| @@ -73,7 +73,7 @@ const makeAssetsLayer = (png: Option.Option<string>) => · 3 unmodified lines | ||
| 73 | 73 | png, |
| 74 | 74 | }), |
| 75 | 75 | resolveResourcePath: () => Effect.succeed(Option.none()), |
| 76 | − } satisfies DesktopAssets.DesktopAssetsShape); | |
| 76 | + } satisfies DesktopAssets.DesktopAssets["Service"]); | |
| 77 | 77 | |
| 78 | 78 | const makeEnvironmentLayer = (overrides: TestEnvironmentInput = {}) => { |
| 79 | 79 | const { env, ...environmentOverrides } = overrides; |
| @@ -18,14 +18,12 @@ const AppPackageMetadata = Schema.Struct({ · 17 unmodified lines | ||
| 18 | 18 | }); |
| 19 | 19 | const decodeAppPackageMetadata = Schema.decodeEffect(Schema.fromJsonString(AppPackageMetadata)); |
| 20 | 20 | |
| 21 | −export interface DesktopAppIdentityShape { | |
| 22 | − readonly resolveUserDataPath: Effect.Effect<string>; | |
| 23 | − readonly configure: Effect.Effect<void>; | |
| 24 | −} | |
| 25 | − | |
| 26 | 21 | export class DesktopAppIdentity extends Context.Service< |
| 27 | 22 | DesktopAppIdentity, |
| 28 | − DesktopAppIdentityShape | |
| 23 | + { | |
| 24 | + readonly resolveUserDataPath: Effect.Effect<string>; | |
| 25 | + readonly configure: Effect.Effect<void>; | |
| 26 | + } | |
| 29 | 27 | >()("@t3tools/desktop/app/DesktopAppIdentity") {} |
| 30 | 28 | |
| 31 | 29 | const normalizeCommitHash = (value: string): Option.Option<string> => { |
| @@ -12,14 +12,13 @@ export interface DesktopIconPaths { · 11 unmodified lines | ||
| 12 | 12 | readonly png: Option.Option<string>; |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | −export interface DesktopAssetsShape { | |
| 16 | − readonly iconPaths: Effect.Effect<DesktopIconPaths>; | |
| 17 | − readonly resolveResourcePath: (fileName: string) => Effect.Effect<Option.Option<string>>; | |
| 18 | −} | |
| 19 | − | |
| 20 | −export class DesktopAssets extends Context.Service<DesktopAssets, DesktopAssetsShape>()( | |
| 21 | − "@t3tools/desktop/app/DesktopAssets", | |
| 22 | −) {} | |
| 15 | +export class DesktopAssets extends Context.Service< | |
| 16 | + DesktopAssets, | |
| 17 | + { | |
| 18 | + readonly iconPaths: Effect.Effect<DesktopIconPaths>; | |
| 19 | + readonly resolveResourcePath: (fileName: string) => Effect.Effect<Option.Option<string>>; | |
| 20 | + } | |
| 21 | +>()("@t3tools/desktop/app/DesktopAssets") {} | |
| 23 | 22 | |
| 24 | 23 | const resolveResourcePath = Effect.fn("desktop.assets.resolveResourcePath")(function* ( |
| 25 | 24 | fileName: string, |
| @@ -21,10 +21,6 @@ vi.mock("@clerk/electron/storage", () => ({ · 20 unmodified lines | ||
| 21 | 21 | storage: storageMock, |
| 22 | 22 | })); |
| 23 | 23 | |
| 24 | −import { | |
| 25 | − createDesktopClerkBridge, | |
| 26 | − resolveDesktopClerkFrontendApiHostname, | |
| 27 | −} from "./DesktopClerk.ts"; | |
| 28 | 24 | import * as DesktopClerk from "./DesktopClerk.ts"; |
| 29 | 25 | import * as DesktopEnvironment from "./DesktopEnvironment.ts"; |
| 30 | 26 | |
| @@ -32,9 +28,12 @@ describe("DesktopClerk", () => { · 1 unmodified line | ||
| 32 | 28 | it("derives the Clerk Frontend API hostname used by the desktop CSP", () => { |
| 33 | 29 | const publishableKey = `pk_test_${btoa("clerk.t3.codes$")}`; |
| 34 | 30 | |
| 35 | − assert.equal(resolveDesktopClerkFrontendApiHostname(publishableKey), "clerk.t3.codes"); | |
| 36 | − assert.equal(resolveDesktopClerkFrontendApiHostname(""), undefined); | |
| 37 | − assert.equal(resolveDesktopClerkFrontendApiHostname("invalid"), undefined); | |
| 31 | + assert.equal( | |
| 32 | + DesktopClerk.resolveDesktopClerkFrontendApiHostname(publishableKey), | |
| 33 | + "clerk.t3.codes", | |
| 34 | + ); | |
| 35 | + assert.equal(DesktopClerk.resolveDesktopClerkFrontendApiHostname(""), undefined); | |
| 36 | + assert.equal(DesktopClerk.resolveDesktopClerkFrontendApiHostname("invalid"), undefined); | |
| 38 | 37 | }); |
| 39 | 38 | |
| 40 | 39 | it.effect("acquires and releases the SDK bridge with the layer", () => { |
| @@ -44,7 +43,7 @@ describe("DesktopClerk", () => { · 3 unmodified lines | ||
| 44 | 43 | const environment = DesktopEnvironment.DesktopEnvironment.of({ |
| 45 | 44 | stateDir: "/tmp/t3-state", |
| 46 | 45 | isDevelopment: true, |
| 47 | − } as unknown as DesktopEnvironment.DesktopEnvironmentShape); | |
| 46 | + } as unknown as DesktopEnvironment.DesktopEnvironment["Service"]); | |
| 48 | 47 | |
| 49 | 48 | return Effect.gen(function* () { |
| 50 | 49 | yield* Effect.scoped( |
| @@ -78,7 +77,7 @@ describe("DesktopClerk", () => { · 27 unmodified lines | ||
| 78 | 77 | storageMock.mockReturnValue(storageAdapter); |
| 79 | 78 | createClerkBridgeMock.mockReturnValue(bridge); |
| 80 | 79 | |
| 81 | − assert.equal(createDesktopClerkBridge("/tmp/t3-state", isDevelopment), bridge); | |
| 80 | + assert.equal(DesktopClerk.createDesktopClerkBridge("/tmp/t3-state", isDevelopment), bridge); | |
| 82 | 81 | assert.deepEqual(storageMock.mock.calls, [[{ path: "/tmp/t3-state" }]]); |
| 83 | 82 | assert.deepEqual(createClerkBridgeMock.mock.calls, [ |
| 84 | 83 | [ |
| @@ -14,17 +14,16 @@ import * as DesktopEnvironment from "./DesktopEnvironment.ts"; · 13 unmodified lines | ||
| 14 | 14 | |
| 15 | 15 | declare const __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__: string | undefined; |
| 16 | 16 | |
| 17 | −export interface DesktopClerkShape { | |
| 18 | − readonly configure: Effect.Effect< | |
| 19 | − void, | |
| 20 | − never, | |
| 21 | − ElectronApp.ElectronApp | ElectronWindow.ElectronWindow | Scope.Scope | |
| 22 | − >; | |
| 23 | −} | |
| 24 | − | |
| 25 | −export class DesktopClerk extends Context.Service<DesktopClerk, DesktopClerkShape>()( | |
| 26 | − "@t3tools/desktop/app/DesktopClerk", | |
| 27 | −) {} | |
| 17 | +export class DesktopClerk extends Context.Service< | |
| 18 | + DesktopClerk, | |
| 19 | + { | |
| 20 | + readonly configure: Effect.Effect< | |
| 21 | + void, | |
| 22 | + never, | |
| 23 | + ElectronApp.ElectronApp | ElectronWindow.ElectronWindow | Scope.Scope | |
| 24 | + >; | |
| 25 | + } | |
| 26 | +>()("@t3tools/desktop/app/DesktopClerk") {} | |
| 28 | 27 | |
| 29 | 28 | export function resolveDesktopClerkFrontendApiHostname( |
| 30 | 29 | publishableKey: string | undefined, |
| @@ -11,10 +11,7 @@ import * as Layer from "effect/Layer"; · 10 unmodified lines | ||
| 11 | 11 | import * as Option from "effect/Option"; |
| 12 | 12 | import * as Path from "effect/Path"; |
| 13 | 13 | |
| 14 | −import { | |
| 15 | − type DesktopSettings, | |
| 16 | − resolveDefaultDesktopSettings, | |
| 17 | −} from "../settings/DesktopAppSettings.ts"; | |
| 14 | +import * as DesktopAppSettings from "../settings/DesktopAppSettings.ts"; | |
| 18 | 15 | import * as DesktopConfig from "./DesktopConfig.ts"; |
| 19 | 16 | import { isNightlyDesktopVersion } from "../updates/updateChannels.ts"; |
| 20 | 17 | |
| @@ -30,55 +27,53 @@ export interface MakeDesktopEnvironmentInput { · 9 unmodified lines | ||
| 30 | 27 | readonly runningUnderArm64Translation: boolean; |
| 31 | 28 | } |
| 32 | 29 | |
| 33 | −export interface DesktopEnvironmentShape { | |
| 34 | − readonly path: Path.Path; | |
| 35 | − readonly dirname: string; | |
| 36 | − readonly platform: NodeJS.Platform; | |
| 37 | − readonly processArch: string; | |
| 38 | − readonly isPackaged: boolean; | |
| 39 | − readonly isDevelopment: boolean; | |
| 40 | − readonly appVersion: string; | |
| 41 | − readonly appPath: string; | |
| 42 | − readonly resourcesPath: string; | |
| 43 | − readonly homeDirectory: string; | |
| 44 | − readonly appDataDirectory: string; | |
| 45 | − readonly baseDir: string; | |
| 46 | − readonly stateDir: string; | |
| 47 | − readonly desktopSettingsPath: string; | |
| 48 | − readonly clientSettingsPath: string; | |
| 49 | − readonly savedEnvironmentRegistryPath: string; | |
| 50 | − readonly serverSettingsPath: string; | |
| 51 | − readonly logDir: string; | |
| 52 | − readonly browserArtifactsDir: string; | |
| 53 | − readonly rootDir: string; | |
| 54 | − readonly appRoot: string; | |
| 55 | − readonly backendEntryPath: string; | |
| 56 | − readonly backendCwd: string; | |
| 57 | − readonly preloadPath: string; | |
| 58 | − readonly appUpdateYmlPath: string; | |
| 59 | − readonly devServerUrl: Option.Option<URL>; | |
| 60 | − readonly devRemoteT3ServerEntryPath: Option.Option<string>; | |
| 61 | − readonly configuredBackendPort: Option.Option<number>; | |
| 62 | − readonly commitHashOverride: Option.Option<string>; | |
| 63 | − readonly otlpTracesUrl: Option.Option<string>; | |
| 64 | − readonly otlpExportIntervalMs: number; | |
| 65 | − readonly branding: DesktopAppBranding; | |
| 66 | − readonly displayName: string; | |
| 67 | − readonly appUserModelId: string; | |
| 68 | − readonly linuxDesktopEntryName: string; | |
| 69 | − readonly linuxWmClass: string; | |
| 70 | − readonly userDataDirName: string; | |
| 71 | − readonly legacyUserDataDirName: string; | |
| 72 | − readonly defaultDesktopSettings: DesktopSettings; | |
| 73 | − readonly runtimeInfo: DesktopRuntimeInfo; | |
| 74 | − readonly resolvePickFolderDefaultPath: (rawOptions: unknown) => Option.Option<string>; | |
| 75 | − readonly resolveResourcePathCandidates: (fileName: string) => readonly string[]; | |
| 76 | − readonly developmentDockIconPath: string; | |
| 77 | −} | |
| 78 | − | |
| 79 | 30 | export class DesktopEnvironment extends Context.Service< |
| 80 | 31 | DesktopEnvironment, |
| 81 | − DesktopEnvironmentShape | |
| 32 | + { | |
| 33 | + readonly path: Path.Path; | |
| 34 | + readonly dirname: string; | |
| 35 | + readonly platform: NodeJS.Platform; | |
| 36 | + readonly processArch: string; | |
| 37 | + readonly isPackaged: boolean; | |
| 38 | + readonly isDevelopment: boolean; | |
| 39 | + readonly appVersion: string; | |
| 40 | + readonly appPath: string; | |
| 41 | + readonly resourcesPath: string; | |
| 42 | + readonly homeDirectory: string; | |
| 43 | + readonly appDataDirectory: string; | |
| 44 | + readonly baseDir: string; | |
| 45 | + readonly stateDir: string; | |
| 46 | + readonly desktopSettingsPath: string; | |
| 47 | + readonly clientSettingsPath: string; | |
| 48 | + readonly savedEnvironmentRegistryPath: string; | |
| 49 | + readonly serverSettingsPath: string; | |
| 50 | + readonly logDir: string; | |
| 51 | + readonly browserArtifactsDir: string; | |
| 52 | + readonly rootDir: string; | |
| 53 | + readonly appRoot: string; | |
| 54 | + readonly backendEntryPath: string; | |
| 55 | + readonly backendCwd: string; | |
| 56 | + readonly preloadPath: string; | |
| 57 | + readonly appUpdateYmlPath: string; | |
| 58 | + readonly devServerUrl: Option.Option<URL>; | |
| 59 | + readonly devRemoteT3ServerEntryPath: Option.Option<string>; | |
| 60 | + readonly configuredBackendPort: Option.Option<number>; | |
| 61 | + readonly commitHashOverride: Option.Option<string>; | |
| 62 | + readonly otlpTracesUrl: Option.Option<string>; | |
| 63 | + readonly otlpExportIntervalMs: number; | |
| 64 | + readonly branding: DesktopAppBranding; | |
| 65 | + readonly displayName: string; | |
| 66 | + readonly appUserModelId: string; | |
| 67 | + readonly linuxDesktopEntryName: string; | |
| 68 | + readonly linuxWmClass: string; | |
| 69 | + readonly userDataDirName: string; | |
| 70 | + readonly legacyUserDataDirName: string; | |
| 71 | + readonly defaultDesktopSettings: DesktopAppSettings.DesktopSettings; | |
| 72 | + readonly runtimeInfo: DesktopRuntimeInfo; | |
| 73 | + readonly resolvePickFolderDefaultPath: (rawOptions: unknown) => Option.Option<string>; | |
| 74 | + readonly resolveResourcePathCandidates: (fileName: string) => readonly string[]; | |
| 75 | + readonly developmentDockIconPath: string; | |
| 76 | + } | |
| 82 | 77 | >()("@t3tools/desktop/app/DesktopEnvironment") {} |
| 83 | 78 | |
| 84 | 79 | const APP_BASE_NAME = "T3 Code"; |
| @@ -136,9 +131,9 @@ function resolveDesktopRuntimeInfo(input: { · 51 unmodified lines | ||
| 136 | 131 | }; |
| 137 | 132 | } |
| 138 | 133 | |
| 139 | −const makeDesktopEnvironment = Effect.fn("desktop.environment.make")(function* ( | |
| 134 | +const make = Effect.fn("desktop.environment.make")(function* ( | |
| 140 | 135 | input: MakeDesktopEnvironmentInput, |
| 141 | −): Effect.fn.Return<DesktopEnvironmentShape, Config.ConfigError, Path.Path> { | |
| 136 | +): Effect.fn.Return<DesktopEnvironment["Service"], Config.ConfigError, Path.Path> { | |
| 142 | 137 | const path = yield* Path.Path; |
| 143 | 138 | const config = yield* DesktopConfig.DesktopConfig; |
| 144 | 139 | const homeDirectory = input.homeDirectory; |
| @@ -208,7 +203,7 @@ const makeDesktopEnvironment = Effect.fn("desktop.environment.make")(function* ( · 63 unmodified lines | ||
| 208 | 203 | linuxWmClass: isDevelopment ? "t3code-dev" : "t3code", |
| 209 | 204 | userDataDirName, |
| 210 | 205 | legacyUserDataDirName, |
| 211 | − defaultDesktopSettings: resolveDefaultDesktopSettings(input.appVersion), | |
| 206 | + defaultDesktopSettings: DesktopAppSettings.resolveDefaultDesktopSettings(input.appVersion), | |
| 212 | 207 | runtimeInfo: resolveDesktopRuntimeInfo({ |
| 213 | 208 | platform: input.platform, |
| 214 | 209 | processArch: input.processArch, |
| @@ -250,4 +245,4 @@ const makeDesktopEnvironment = Effect.fn("desktop.environment.make")(function* ( · 35 unmodified lines | ||
| 250 | 245 | }); |
| 251 | 246 | |
| 252 | 247 | export const layer = (input: MakeDesktopEnvironmentInput) => |
| 253 | − Layer.effect(DesktopEnvironment, makeDesktopEnvironment(input)); | |
| 248 | + Layer.effect(DesktopEnvironment, make(input)); | |
| @@ -1,7 +1,6 @@ | ||
| 1 | 1 | import * as Cause from "effect/Cause"; |
| 2 | 2 | import * as Context from "effect/Context"; |
| 3 | 3 | import * as Effect from "effect/Effect"; |
| 4 | −import * as Deferred from "effect/Deferred"; | |
| 5 | 4 | import * as Layer from "effect/Layer"; |
| 6 | 5 | import * as Ref from "effect/Ref"; |
| 7 | 6 | import * as Scope from "effect/Scope"; |
| @@ -10,63 +9,34 @@ import type * as Electron from "electron"; · 2 unmodified lines | ||
| 10 | 9 | |
| 11 | 10 | import * as DesktopEnvironment from "./DesktopEnvironment.ts"; |
| 12 | 11 | import * as DesktopObservability from "./DesktopObservability.ts"; |
| 12 | +import * as DesktopShutdownModule from "./DesktopShutdown.ts"; | |
| 13 | 13 | import * as ElectronApp from "../electron/ElectronApp.ts"; |
| 14 | 14 | import * as ElectronTheme from "../electron/ElectronTheme.ts"; |
| 15 | 15 | import * as DesktopState from "./DesktopState.ts"; |
| 16 | 16 | import * as DesktopWindow from "../window/DesktopWindow.ts"; |
| 17 | 17 | |
| 18 | −export interface DesktopShutdownShape { | |
| 19 | − readonly request: Effect.Effect<void>; | |
| 20 | − readonly awaitRequest: Effect.Effect<void>; | |
| 21 | − readonly markComplete: Effect.Effect<void>; | |
| 22 | − readonly awaitComplete: Effect.Effect<void>; | |
| 23 | − readonly isComplete: Effect.Effect<boolean>; | |
| 24 | −} | |
| 25 | − | |
| 26 | −export class DesktopShutdown extends Context.Service<DesktopShutdown, DesktopShutdownShape>()( | |
| 27 | − "@t3tools/desktop/app/DesktopLifecycle/DesktopShutdown", | |
| 28 | −) {} | |
| 29 | − | |
| 30 | −const makeShutdown = Effect.gen(function* () { | |
| 31 | − const requested = yield* Deferred.make<void>(); | |
| 32 | − const completed = yield* Deferred.make<void>(); | |
| 33 | − const completedRef = yield* Ref.make(false); | |
| 34 | − | |
| 35 | − return DesktopShutdown.of({ | |
| 36 | − request: Deferred.succeed(requested, undefined).pipe(Effect.asVoid), | |
| 37 | − awaitRequest: Deferred.await(requested), | |
| 38 | − markComplete: Ref.set(completedRef, true).pipe( | |
| 39 | − Effect.andThen(Deferred.succeed(completed, undefined)), | |
| 40 | − Effect.asVoid, | |
| 41 | − ), | |
| 42 | − awaitComplete: Deferred.await(completed), | |
| 43 | − isComplete: Ref.get(completedRef), | |
| 44 | − }); | |
| 45 | −}); | |
| 46 | − | |
| 47 | −export const layerShutdown = Layer.effect(DesktopShutdown, makeShutdown); | |
| 18 | +export { DesktopShutdown, layer as layerShutdown } from "./DesktopShutdown.ts"; | |
| 48 | 19 | |
| 49 | 20 | export type DesktopLifecycleRuntimeServices = |
| 50 | 21 | | DesktopEnvironment.DesktopEnvironment |
| 51 | − | DesktopShutdown | |
| 22 | + | DesktopShutdownModule.DesktopShutdown | |
| 52 | 23 | | DesktopState.DesktopState |
| 53 | 24 | | DesktopWindow.DesktopWindow |
| 54 | 25 | | ElectronApp.ElectronApp |
| 55 | 26 | | ElectronTheme.ElectronTheme; |
| 56 | 27 | |
| 57 | −export interface DesktopLifecycleShape { | |
| 58 | − readonly relaunch: ( | |
| 59 | − reason: string, | |
| 60 | − ) => Effect.Effect<void, never, DesktopLifecycleRuntimeServices>; | |
| 61 | − readonly register: Effect.Effect<void, never, Scope.Scope | DesktopLifecycleRuntimeServices>; | |
| 62 | −} | |
| 63 | − | |
| 64 | 28 | /** |
| 65 | 29 | * @effect-expect-leaking DesktopEnvironment | DesktopShutdown | DesktopState | DesktopWindow | ElectronApp | ElectronTheme |
| 66 | 30 | */ |
| 67 | −export class DesktopLifecycle extends Context.Service<DesktopLifecycle, DesktopLifecycleShape>()( | |
| 68 | − "@t3tools/desktop/app/DesktopLifecycle", | |
| 69 | −) {} | |
| 31 | +export class DesktopLifecycle extends Context.Service< | |
| 32 | + DesktopLifecycle, | |
| 33 | + { | |
| 34 | + readonly relaunch: ( | |
| 35 | + reason: string, | |
| 36 | + ) => Effect.Effect<void, never, DesktopLifecycleRuntimeServices>; | |
| 37 | + readonly register: Effect.Effect<void, never, Scope.Scope | DesktopLifecycleRuntimeServices>; | |
| 38 | + } | |
| 39 | +>()("@t3tools/desktop/app/DesktopLifecycle") {} | |
| 70 | 40 | |
| 71 | 41 | const { logInfo: logLifecycleInfo, logError: logLifecycleError } = |
| 72 | 42 | DesktopObservability.makeComponentLogger("desktop-lifecycle"); |
| @@ -93,8 +63,8 @@ function addScopedListener<Args extends ReadonlyArray<unknown>>( · 20 unmodified lines | ||
| 93 | 63 | } |
| 94 | 64 | |
| 95 | 65 | const requestDesktopShutdownAndWait = Effect.fn("desktop.lifecycle.requestShutdownAndWait")( |
| 96 | − function* (): Effect.fn.Return<void, never, DesktopShutdown> { | |
| 97 | − const shutdown = yield* DesktopShutdown; | |
| 66 | + function* (): Effect.fn.Return<void, never, DesktopShutdownModule.DesktopShutdown> { | |
| 67 | + const shutdown = yield* DesktopShutdownModule.DesktopShutdown; | |
| 98 | 68 | yield* shutdown.request; |
| 99 | 69 | yield* shutdown.awaitComplete; |
| 100 | 70 | }, |
| @@ -154,83 +124,82 @@ function quitFromSignal( · 53 unmodified lines | ||
| 154 | 124 | ); |
| 155 | 125 | } |
| 156 | 126 | |
| 157 | −export const layer = Layer.succeed( | |
| 158 | − DesktopLifecycle, | |
| 159 | − DesktopLifecycle.of({ | |
| 160 | − relaunch: Effect.fn("desktop.lifecycle.relaunch")(function* (reason) { | |
| 161 | − const electronApp = yield* ElectronApp.ElectronApp; | |
| 162 | − const environment = yield* DesktopEnvironment.DesktopEnvironment; | |
| 163 | − const state = yield* DesktopState.DesktopState; | |
| 164 | − yield* logLifecycleInfo("desktop relaunch requested", { reason }); | |
| 165 | − yield* Effect.gen(function* () { | |
| 166 | − yield* Effect.yieldNow; | |
| 167 | − yield* Ref.set(state.quitting, true); | |
| 168 | − yield* requestDesktopShutdownAndWait(); | |
| 169 | − if (environment.isDevelopment) { | |
| 170 | − yield* electronApp.exit(75); | |
| 171 | − return; | |
| 172 | − } | |
| 173 | − yield* electronApp.relaunch({ | |
| 174 | − execPath: process.execPath, | |
| 175 | − args: process.argv.slice(1), | |
| 176 | − }); | |
| 177 | − yield* electronApp.exit(0); | |
| 178 | − }).pipe( | |
| 179 | − Effect.catchCause((cause) => | |
| 180 | − logLifecycleError("desktop relaunch failed", { | |
| 181 | − cause: Cause.pretty(cause), | |
| 182 | − }), | |
| 183 | − ), | |
| 184 | − Effect.forkDetach, | |
| 185 | − Effect.asVoid, | |
| 186 | − ); | |
| 187 | − }), | |
| 188 | − register: Effect.gen(function* () { | |
| 189 | − const desktopWindow = yield* DesktopWindow.DesktopWindow; | |
| 190 | − const electronApp = yield* ElectronApp.ElectronApp; | |
| 191 | − const electronTheme = yield* ElectronTheme.ElectronTheme; | |
| 192 | − const environment = yield* DesktopEnvironment.DesktopEnvironment; | |
| 193 | − const context = yield* Effect.context<DesktopLifecycleRuntimeServices>(); | |
| 194 | − const runEffect = Effect.runPromiseWith(context); | |
| 195 | − let quitAllowed = false; | |
| 196 | − yield* electronTheme.onUpdated(() => { | |
| 197 | − void runEffect( | |
| 198 | − desktopWindow.syncAppearance.pipe(Effect.withSpan("desktop.lifecycle.themeUpdated")), | |
| 199 | − ); | |
| 200 | − }); | |
| 201 | − yield* electronApp.on("before-quit", (event: Electron.Event) => { | |
| 202 | − handleBeforeQuit( | |
| 203 | − event, | |
| 204 | − runEffect, | |
| 205 | − () => quitAllowed, | |
| 206 | − () => { | |
| 207 | − quitAllowed = true; | |
| 208 | − }, | |
| 209 | − ); | |
| 127 | +const make = DesktopLifecycle.of({ | |
| 128 | + relaunch: Effect.fn("desktop.lifecycle.relaunch")(function* (reason) { | |
| 129 | + const electronApp = yield* ElectronApp.ElectronApp; | |
| 130 | + const environment = yield* DesktopEnvironment.DesktopEnvironment; | |
| 131 | + const state = yield* DesktopState.DesktopState; | |
| 132 | + yield* logLifecycleInfo("desktop relaunch requested", { reason }); | |
| 133 | + yield* Effect.gen(function* () { | |
| 134 | + yield* Effect.yieldNow; | |
| 135 | + yield* Ref.set(state.quitting, true); | |
| 136 | + yield* requestDesktopShutdownAndWait(); | |
| 137 | + if (environment.isDevelopment) { | |
| 138 | + yield* electronApp.exit(75); | |
| 139 | + return; | |
| 140 | + } | |
| 141 | + yield* electronApp.relaunch({ | |
| 142 | + execPath: process.execPath, | |
| 143 | + args: process.argv.slice(1), | |
| 210 | 144 | }); |
| 211 | − yield* electronApp.on("activate", () => { | |
| 212 | − void runEffect(desktopWindow.activate.pipe(Effect.withSpan("desktop.lifecycle.activate"))); | |
| 145 | + yield* electronApp.exit(0); | |
| 146 | + }).pipe( | |
| 147 | + Effect.catchCause((cause) => | |
| 148 | + logLifecycleError("desktop relaunch failed", { | |
| 149 | + cause: Cause.pretty(cause), | |
| 150 | + }), | |
| 151 | + ), | |
| 152 | + Effect.forkDetach, | |
| 153 | + Effect.asVoid, | |
| 154 | + ); | |
| 155 | + }), | |
| 156 | + register: Effect.gen(function* () { | |
| 157 | + const desktopWindow = yield* DesktopWindow.DesktopWindow; | |
| 158 | + const electronApp = yield* ElectronApp.ElectronApp; | |
| 159 | + const electronTheme = yield* ElectronTheme.ElectronTheme; | |
| 160 | + const environment = yield* DesktopEnvironment.DesktopEnvironment; | |
| 161 | + const context = yield* Effect.context<DesktopLifecycleRuntimeServices>(); | |
| 162 | + const runEffect = Effect.runPromiseWith(context); | |
| 163 | + let quitAllowed = false; | |
| 164 | + yield* electronTheme.onUpdated(() => { | |
| 165 | + void runEffect( | |
| 166 | + desktopWindow.syncAppearance.pipe(Effect.withSpan("desktop.lifecycle.themeUpdated")), | |
| 167 | + ); | |
| 168 | + }); | |
| 169 | + yield* electronApp.on("before-quit", (event: Electron.Event) => { | |
| 170 | + handleBeforeQuit( | |
| 171 | + event, | |
| 172 | + runEffect, | |
| 173 | + () => quitAllowed, | |
| 174 | + () => { | |
| 175 | + quitAllowed = true; | |
| 176 | + }, | |
| 177 | + ); | |
| 178 | + }); | |
| 179 | + yield* electronApp.on("activate", () => { | |
| 180 | + void runEffect(desktopWindow.activate.pipe(Effect.withSpan("desktop.lifecycle.activate"))); | |
| 181 | + }); | |
| 182 | + yield* electronApp.on("window-all-closed", () => { | |
| 183 | + void runEffect( | |
| 184 | + Effect.gen(function* () { | |
| 185 | + const app = yield* ElectronApp.ElectronApp; | |
| 186 | + const state = yield* DesktopState.DesktopState; | |
| 187 | + if (environment.platform !== "darwin" && !(yield* Ref.get(state.quitting))) { | |
| 188 | + yield* app.quit; | |
| 189 | + } | |
| 190 | + }).pipe(Effect.withSpan("desktop.lifecycle.windowAllClosed")), | |
| 191 | + ); | |
| 192 | + }); | |
| 193 | + | |
| 194 | + if (environment.platform !== "win32") { | |
| 195 | + yield* addScopedListener(process, "SIGINT", () => { | |
| 196 | + quitFromSignal("SIGINT", runEffect); | |
| 213 | 197 | }); |
| 214 | − yield* electronApp.on("window-all-closed", () => { | |
| 215 | − void runEffect( | |
| 216 | − Effect.gen(function* () { | |
| 217 | − const app = yield* ElectronApp.ElectronApp; | |
| 218 | − const state = yield* DesktopState.DesktopState; | |
| 219 | − if (environment.platform !== "darwin" && !(yield* Ref.get(state.quitting))) { | |
| 220 | − yield* app.quit; | |
| 221 | − } | |
| 222 | − }).pipe(Effect.withSpan("desktop.lifecycle.windowAllClosed")), | |
| 223 | − ); | |
| 198 | + yield* addScopedListener(process, "SIGTERM", () => { | |
| 199 | + quitFromSignal("SIGTERM", runEffect); | |
| 224 | 200 | }); |
| 201 | + } | |
| 202 | + }).pipe(Effect.withSpan("desktop.lifecycle.register")), | |
| 203 | +}); | |
| 225 | 204 | |
| 226 | − if (environment.platform !== "win32") { | |
| 227 | − yield* addScopedListener(process, "SIGINT", () => { | |
| 228 | − quitFromSignal("SIGINT", runEffect); | |
| 229 | − }); | |
| 230 | − yield* addScopedListener(process, "SIGTERM", () => { | |
| 231 | − quitFromSignal("SIGTERM", runEffect); | |
| 232 | − }); | |
| 233 | − } | |
| 234 | − }).pipe(Effect.withSpan("desktop.lifecycle.register")), | |
| 235 | − }), | |
| 236 | −); | |
| 205 | +export const layer = Layer.succeed(DesktopLifecycle, make); | |
| @@ -314,7 +314,7 @@ const PRIMARY_BACKEND_LOG_INSTANCE_ID = PRIMARY_LOCAL_ENVIRONMENT_ID; · 313 unmodified lines | ||
| 314 | 314 | const sanitizeInstanceIdForFileName = (id: string): string => id.replace(/[^a-zA-Z0-9._-]+/g, "_"); |
| 315 | 315 | |
| 316 | 316 | const backendLogFilePathForInstance = ( |
| 317 | − environment: DesktopEnvironment.DesktopEnvironmentShape, | |
| 317 | + environment: DesktopEnvironment.DesktopEnvironment["Service"], | |
| 318 | 318 | id: string, |
| 319 | 319 | ): string => { |
| 320 | 320 | // Primary keeps the historical "server-child.log" path so ops scripts |
| @@ -332,7 +332,7 @@ const backendLogFilePathForInstance = ( · 11 unmodified lines | ||
| 332 | 332 | // per-call shape lets the shape annotate writes with the *caller's* |
| 333 | 333 | // id rather than whatever id created the cached writer first. |
| 334 | 334 | const makeBackendOutputSinkForInstance = ( |
| 335 | − environment: DesktopEnvironment.DesktopEnvironmentShape, | |
| 335 | + environment: DesktopEnvironment.DesktopEnvironment["Service"], | |
| 336 | 336 | id: string, |
| 337 | 337 | ): Effect.Effect< |
| 338 | 338 | Option.Option<RotatingLogFileWriter>, |
| @@ -344,7 +344,7 @@ const makeBackendOutputSinkForInstance = ( · 5 unmodified lines | ||
| 344 | 344 | }).pipe(Effect.option); |
| 345 | 345 | |
| 346 | 346 | const makeBackendOutputLogShape = ( |
| 347 | − environment: DesktopEnvironment.DesktopEnvironmentShape, | |
| 347 | + environment: DesktopEnvironment.DesktopEnvironment["Service"], | |
| 348 | 348 | id: string, |
| 349 | 349 | sink: Option.Option<RotatingLogFileWriter>, |
| 350 | 350 | ): DesktopBackendOutputLogShape => |
| @@ -0,0 +1,35 @@ | ||
| 1 | +import * as Context from "effect/Context"; | |
| 2 | +import * as Deferred from "effect/Deferred"; | |
| 3 | +import * as Effect from "effect/Effect"; | |
| 4 | +import * as Layer from "effect/Layer"; | |
| 5 | +import * as Ref from "effect/Ref"; | |
| 6 | + | |
| 7 | +export class DesktopShutdown extends Context.Service< | |
| 8 | + DesktopShutdown, | |
| 9 | + { | |
| 10 | + readonly request: Effect.Effect<void>; | |
| 11 | + readonly awaitRequest: Effect.Effect<void>; | |
| 12 | + readonly markComplete: Effect.Effect<void>; | |
| 13 | + readonly awaitComplete: Effect.Effect<void>; | |
| 14 | + readonly isComplete: Effect.Effect<boolean>; | |
| 15 | + } | |
| 16 | +>()("@t3tools/desktop/app/DesktopShutdown") {} | |
| 17 | + | |
| 18 | +const make = Effect.gen(function* () { | |
| 19 | + const requested = yield* Deferred.make<void>(); | |
| 20 | + const completed = yield* Deferred.make<void>(); | |
| 21 | + const completedRef = yield* Ref.make(false); | |
| 22 | + | |
| 23 | + return DesktopShutdown.of({ | |
| 24 | + request: Deferred.succeed(requested, undefined).pipe(Effect.asVoid), | |
| 25 | + awaitRequest: Deferred.await(requested), | |
| 26 | + markComplete: Ref.set(completedRef, true).pipe( | |
| 27 | + Effect.andThen(Deferred.succeed(completed, undefined)), | |
| 28 | + Effect.asVoid, | |
| 29 | + ), | |
| 30 | + awaitComplete: Deferred.await(completed), | |
| 31 | + isComplete: Ref.get(completedRef), | |
| 32 | + }); | |
| 33 | +}); | |
| 34 | + | |
| 35 | +export const layer = Layer.effect(DesktopShutdown, make); | |
| @@ -3,17 +3,17 @@ import * as Effect from "effect/Effect"; · 2 unmodified lines | ||
| 3 | 3 | import * as Layer from "effect/Layer"; |
| 4 | 4 | import * as Ref from "effect/Ref"; |
| 5 | 5 | |
| 6 | −export interface DesktopStateShape { | |
| 7 | − readonly quitting: Ref.Ref<boolean>; | |
| 8 | −} | |
| 6 | +export class DesktopState extends Context.Service< | |
| 7 | + DesktopState, | |
| 8 | + { | |
| 9 | + readonly backendReady: Ref.Ref<boolean>; | |
| 10 | + readonly quitting: Ref.Ref<boolean>; | |
| 11 | + } | |
| 12 | +>()("@t3tools/desktop/app/DesktopState") {} | |
| 9 | 13 | |
| 10 | −export class DesktopState extends Context.Service<DesktopState, DesktopStateShape>()( | |
| 11 | − "@t3tools/desktop/app/DesktopState", | |
| 12 | −) {} | |
| 14 | +const make = Effect.all({ | |
| 15 | + backendReady: Ref.make(false), | |
| 16 | + quitting: Ref.make(false), | |
| 17 | +}); | |
| 13 | 18 | |
| 14 | −export const layer = Layer.effect( | |
| 15 | − DesktopState, | |
| 16 | − Effect.all({ | |
| 17 | − quitting: Ref.make(false), | |
| 18 | − }), | |
| 19 | −); | |
| 19 | +export const layer = Layer.effect(DesktopState, make); | |
| @@ -1,5 +1,6 @@ | ||
| 1 | 1 | import * as Context from "effect/Context"; |
| 2 | 2 | import * as Effect from "effect/Effect"; |
| 3 | +import * as Layer from "effect/Layer"; | |
| 3 | 4 | import * as Schema from "effect/Schema"; |
| 4 | 5 | import * as Scope from "effect/Scope"; |
| 5 | 6 | |
| @@ -33,20 +34,19 @@ export interface DesktopSyncIpcMethod<E, R> { · 27 unmodified lines | ||
| 33 | 34 | readonly handler: () => Effect.Effect<unknown, E, R>; |
| 34 | 35 | } |
| 35 | 36 | |
| 36 | −export interface DesktopIpcShape { | |
| 37 | − readonly handle: <E, R>( | |
| 38 | − input: DesktopIpcMethod<E, R>, | |
| 39 | − ) => Effect.Effect<void, never, R | Scope.Scope>; | |
| 40 | − readonly handleSync: <E, R>( | |
| 41 | − input: DesktopSyncIpcMethod<E, R>, | |
| 42 | − ) => Effect.Effect<void, never, R | Scope.Scope>; | |
| 43 | −} | |
| 44 | − | |
| 45 | −export class DesktopIpc extends Context.Service<DesktopIpc, DesktopIpcShape>()( | |
| 46 | − "@t3tools/desktop/ipc/DesktopIpc", | |
| 47 | −) {} | |
| 48 | − | |
| 49 | −export const make = (ipcMain: DesktopIpcMain): DesktopIpcShape => | |
| 37 | +export class DesktopIpc extends Context.Service< | |
| 38 | + DesktopIpc, | |
| 39 | + { | |
| 40 | + readonly handle: <E, R>( | |
| 41 | + input: DesktopIpcMethod<E, R>, | |
| 42 | + ) => Effect.Effect<void, never, R | Scope.Scope>; | |
| 43 | + readonly handleSync: <E, R>( | |
| 44 | + input: DesktopSyncIpcMethod<E, R>, | |
| 45 | + ) => Effect.Effect<void, never, R | Scope.Scope>; | |
| 46 | + } | |
| 47 | +>()("@t3tools/desktop/ipc/DesktopIpc") {} | |
| 48 | + | |
| 49 | +export const make = (ipcMain: DesktopIpcMain): DesktopIpc["Service"] => | |
| 50 | 50 | DesktopIpc.of({ |
| 51 | 51 | handle: Effect.fn("desktop.ipc.registerInvoke")(function* <E, R>({ |
| 52 | 52 | channel, |
| @@ -97,6 +97,8 @@ export const make = (ipcMain: DesktopIpcMain): DesktopIpcShape => · 44 unmodified lines | ||
| 97 | 97 | }), |
| 98 | 98 | }); |
| 99 | 99 | |
| 100 | +export const layer = (ipcMain: DesktopIpcMain) => Layer.succeed(DesktopIpc, make(ipcMain)); | |
| 101 | + | |
| 100 | 102 | /** |
| 101 | 103 | * Convenience helpers for creating IPC methods |
| 102 | 104 | */ |
| @@ -5,9 +5,9 @@ import * as Schema from "effect/Schema"; · 4 unmodified lines | ||
| 5 | 5 | |
| 6 | 6 | import * as DesktopClientSettings from "../../settings/DesktopClientSettings.ts"; |
| 7 | 7 | import * as IpcChannels from "../channels.ts"; |
| 8 | −import { makeIpcMethod } from "../DesktopIpc.ts"; | |
| 8 | +import * as DesktopIpc from "../DesktopIpc.ts"; | |
| 9 | 9 | |
| 10 | −export const getClientSettings = makeIpcMethod({ | |
| 10 | +export const getClientSettings = DesktopIpc.makeIpcMethod({ | |
| 11 | 11 | channel: IpcChannels.GET_CLIENT_SETTINGS_CHANNEL, |
| 12 | 12 | payload: Schema.Void, |
| 13 | 13 | result: Schema.NullOr(ClientSettingsSchema), |
| @@ -17,7 +17,7 @@ export const getClientSettings = makeIpcMethod({ · 3 unmodified lines | ||
| 17 | 17 | }), |
| 18 | 18 | }); |
| 19 | 19 | |
| 20 | −export const setClientSettings = makeIpcMethod({ | |
| 20 | +export const setClientSettings = DesktopIpc.makeIpcMethod({ | |
| 21 | 21 | channel: IpcChannels.SET_CLIENT_SETTINGS_CHANNEL, |
| 22 | 22 | payload: ClientSettingsSchema, |
| 23 | 23 | result: Schema.Void, |
| @@ -4,9 +4,9 @@ import * as Schema from "effect/Schema"; · 3 unmodified lines | ||
| 4 | 4 | |
| 5 | 5 | import * as DesktopConnectionCatalogStore from "../../app/DesktopConnectionCatalogStore.ts"; |
| 6 | 6 | import * as IpcChannels from "../channels.ts"; |
| 7 | −import { makeIpcMethod } from "../DesktopIpc.ts"; | |
| 7 | +import * as DesktopIpc from "../DesktopIpc.ts"; | |
| 8 | 8 | |
| 9 | −export const getConnectionCatalog = makeIpcMethod({ | |
| 9 | +export const getConnectionCatalog = DesktopIpc.makeIpcMethod({ | |
| 10 | 10 | channel: IpcChannels.GET_CONNECTION_CATALOG_CHANNEL, |
| 11 | 11 | payload: Schema.Void, |
| 12 | 12 | result: Schema.NullOr(Schema.String), |
| @@ -16,7 +16,7 @@ export const getConnectionCatalog = makeIpcMethod({ · 3 unmodified lines | ||
| 16 | 16 | }), |
| 17 | 17 | }); |
| 18 | 18 | |
| 19 | −export const setConnectionCatalog = makeIpcMethod({ | |
| 19 | +export const setConnectionCatalog = DesktopIpc.makeIpcMethod({ | |
| 20 | 20 | channel: IpcChannels.SET_CONNECTION_CATALOG_CHANNEL, |
| 21 | 21 | payload: Schema.String, |
| 22 | 22 | result: Schema.Boolean, |
| @@ -26,7 +26,7 @@ export const setConnectionCatalog = makeIpcMethod({ · 3 unmodified lines | ||
| 26 | 26 | }), |
| 27 | 27 | }); |
| 28 | 28 | |
| 29 | −export const clearConnectionCatalog = makeIpcMethod({ | |
| 29 | +export const clearConnectionCatalog = DesktopIpc.makeIpcMethod({ | |
| 30 | 30 | channel: IpcChannels.CLEAR_CONNECTION_CATALOG_CHANNEL, |
| 31 | 31 | payload: Schema.Void, |
| 32 | 32 | result: Schema.Void, |
| @@ -27,7 +27,7 @@ import { pathToFileURL } from "node:url"; · 26 unmodified lines | ||
| 27 | 27 | import * as PreviewManager from "../../preview/Manager.ts"; |
| 28 | 28 | import { PREVIEW_WEBVIEW_PREFERENCES } from "../../preview/WebviewPreferences.ts"; |
| 29 | 29 | import * as IpcChannels from "../channels.ts"; |
| 30 | −import { makeIpcMethod } from "../DesktopIpc.ts"; | |
| 30 | +import * as DesktopIpc from "../DesktopIpc.ts"; | |
| 31 | 31 | |
| 32 | 32 | const broadcast = (channel: string, ...args: ReadonlyArray<unknown>): void => { |
| 33 | 33 | for (const window of BrowserWindow.getAllWindows()) { |
| @@ -52,7 +52,7 @@ export const installPreviewEventForwarding = Effect.fn( · 18 unmodified lines | ||
| 52 | 52 | }); |
| 53 | 53 | }); |
| 54 | 54 | |
| 55 | −export const createTab = makeIpcMethod({ | |
| 55 | +export const createTab = DesktopIpc.makeIpcMethod({ | |
| 56 | 56 | channel: IpcChannels.PREVIEW_CREATE_TAB_CHANNEL, |
| 57 | 57 | payload: DesktopPreviewTabInputSchema, |
| 58 | 58 | result: Schema.Void, |
| @@ -62,7 +62,7 @@ export const createTab = makeIpcMethod({ · 3 unmodified lines | ||
| 62 | 62 | }), |
| 63 | 63 | }); |
| 64 | 64 | |
| 65 | −export const closeTab = makeIpcMethod({ | |
| 65 | +export const closeTab = DesktopIpc.makeIpcMethod({ | |
| 66 | 66 | channel: IpcChannels.PREVIEW_CLOSE_TAB_CHANNEL, |
| 67 | 67 | payload: DesktopPreviewTabInputSchema, |
| 68 | 68 | result: Schema.Void, |
| @@ -72,7 +72,7 @@ export const closeTab = makeIpcMethod({ · 3 unmodified lines | ||
| 72 | 72 | }), |
| 73 | 73 | }); |
| 74 | 74 | |
| 75 | −export const registerWebview = makeIpcMethod({ | |
| 75 | +export const registerWebview = DesktopIpc.makeIpcMethod({ | |
| 76 | 76 | channel: IpcChannels.PREVIEW_REGISTER_WEBVIEW_CHANNEL, |
| 77 | 77 | payload: DesktopPreviewRegisterWebviewInputSchema, |
| 78 | 78 | result: Schema.Void, |
| @@ -82,7 +82,7 @@ export const registerWebview = makeIpcMethod({ · 3 unmodified lines | ||
| 82 | 82 | }), |
| 83 | 83 | }); |
| 84 | 84 | |
| 85 | −export const navigate = makeIpcMethod({ | |
| 85 | +export const navigate = DesktopIpc.makeIpcMethod({ | |
| 86 | 86 | channel: IpcChannels.PREVIEW_NAVIGATE_CHANNEL, |
| 87 | 87 | payload: DesktopPreviewNavigateInputSchema, |
| 88 | 88 | result: Schema.Void, |
| @@ -100,7 +100,7 @@ const tabMethod = ( · 11 unmodified lines | ||
| 100 | 100 | tabId: string, |
| 101 | 101 | ) => Effect.Effect<void, PreviewManager.PreviewManagerError>, |
| 102 | 102 | ) => |
| 103 | − makeIpcMethod({ | |
| 103 | + DesktopIpc.makeIpcMethod({ | |
| 104 | 104 | channel, |
| 105 | 105 | payload: DesktopPreviewTabInputSchema, |
| 106 | 106 | result: Schema.Void, |
| @@ -166,7 +166,7 @@ export const stopRecording = tabMethod( · 59 unmodified lines | ||
| 166 | 166 | (manager, tabId) => manager.stopRecording(tabId), |
| 167 | 167 | ); |
| 168 | 168 | |
| 169 | −export const clearCookies = makeIpcMethod({ | |
| 169 | +export const clearCookies = DesktopIpc.makeIpcMethod({ | |
| 170 | 170 | channel: IpcChannels.PREVIEW_CLEAR_COOKIES_CHANNEL, |
| 171 | 171 | payload: Schema.Void, |
| 172 | 172 | result: Schema.Void, |
| @@ -176,7 +176,7 @@ export const clearCookies = makeIpcMethod({ · 3 unmodified lines | ||
| 176 | 176 | }), |
| 177 | 177 | }); |
| 178 | 178 | |
| 179 | −export const clearCache = makeIpcMethod({ | |
| 179 | +export const clearCache = DesktopIpc.makeIpcMethod({ | |
| 180 | 180 | channel: IpcChannels.PREVIEW_CLEAR_CACHE_CHANNEL, |
| 181 | 181 | payload: Schema.Void, |
| 182 | 182 | result: Schema.Void, |
| @@ -186,7 +186,7 @@ export const clearCache = makeIpcMethod({ · 3 unmodified lines | ||
| 186 | 186 | }), |
| 187 | 187 | }); |
| 188 | 188 | |
| 189 | −export const getPreviewConfig = makeIpcMethod({ | |
| 189 | +export const getPreviewConfig = DesktopIpc.makeIpcMethod({ | |
| 190 | 190 | channel: IpcChannels.PREVIEW_GET_CONFIG_CHANNEL, |
| 191 | 191 | payload: DesktopPreviewConfigInputSchema, |
| 192 | 192 | result: DesktopPreviewWebviewConfigSchema, |
| @@ -201,7 +201,7 @@ export const getPreviewConfig = makeIpcMethod({ · 8 unmodified lines | ||
| 201 | 201 | }), |
| 202 | 202 | }); |
| 203 | 203 | |
| 204 | −export const setAnnotationTheme = makeIpcMethod({ | |
| 204 | +export const setAnnotationTheme = DesktopIpc.makeIpcMethod({ | |
| 205 | 205 | channel: IpcChannels.PREVIEW_SET_ANNOTATION_THEME_CHANNEL, |
| 206 | 206 | payload: DesktopPreviewAnnotationThemeInputSchema, |
| 207 | 207 | result: Schema.Void, |
| @@ -211,7 +211,7 @@ export const setAnnotationTheme = makeIpcMethod({ · 3 unmodified lines | ||
| 211 | 211 | }), |
| 212 | 212 | }); |
| 213 | 213 | |
| 214 | −export const pickElement = makeIpcMethod({ | |
| 214 | +export const pickElement = DesktopIpc.makeIpcMethod({ | |
| 215 | 215 | channel: IpcChannels.PREVIEW_PICK_ELEMENT_CHANNEL, |
| 216 | 216 | payload: DesktopPreviewTabInputSchema, |
| 217 | 217 | result: Schema.NullOr(PreviewAnnotationPayloadSchema), |
| @@ -221,7 +221,7 @@ export const pickElement = makeIpcMethod({ · 3 unmodified lines | ||
| 221 | 221 | }), |
| 222 | 222 | }); |
| 223 | 223 | |
| 224 | −export const captureScreenshot = makeIpcMethod({ | |
| 224 | +export const captureScreenshot = DesktopIpc.makeIpcMethod({ | |
| 225 | 225 | channel: IpcChannels.PREVIEW_CAPTURE_SCREENSHOT_CHANNEL, |
| 226 | 226 | payload: DesktopPreviewTabInputSchema, |
| 227 | 227 | result: DesktopPreviewScreenshotArtifactSchema, |
| @@ -231,7 +231,7 @@ export const captureScreenshot = makeIpcMethod({ · 3 unmodified lines | ||
| 231 | 231 | }), |
| 232 | 232 | }); |
| 233 | 233 | |
| 234 | −export const revealArtifact = makeIpcMethod({ | |
| 234 | +export const revealArtifact = DesktopIpc.makeIpcMethod({ | |
| 235 | 235 | channel: IpcChannels.PREVIEW_REVEAL_ARTIFACT_CHANNEL, |
| 236 | 236 | payload: DesktopPreviewArtifactInputSchema, |
| 237 | 237 | result: Schema.Void, |
| @@ -241,7 +241,7 @@ export const revealArtifact = makeIpcMethod({ · 3 unmodified lines | ||
| 241 | 241 | }), |
| 242 | 242 | }); |
| 243 | 243 | |
| 244 | −export const copyArtifactToClipboard = makeIpcMethod({ | |
| 244 | +export const copyArtifactToClipboard = DesktopIpc.makeIpcMethod({ | |
| 245 | 245 | channel: IpcChannels.PREVIEW_COPY_ARTIFACT_CHANNEL, |
| 246 | 246 | payload: DesktopPreviewArtifactInputSchema, |
| 247 | 247 | result: Schema.Void, |
| @@ -251,7 +251,7 @@ export const copyArtifactToClipboard = makeIpcMethod({ · 3 unmodified lines | ||
| 251 | 251 | }), |
| 252 | 252 | }); |
| 253 | 253 | |
| 254 | −export const automationStatus = makeIpcMethod({ | |
| 254 | +export const automationStatus = DesktopIpc.makeIpcMethod({ | |
| 255 | 255 | channel: IpcChannels.PREVIEW_AUTOMATION_STATUS_CHANNEL, |
| 256 | 256 | payload: DesktopPreviewTabInputSchema, |
| 257 | 257 | result: PreviewAutomationStatus, |
| @@ -261,7 +261,7 @@ export const automationStatus = makeIpcMethod({ · 3 unmodified lines | ||
| 261 | 261 | }), |
| 262 | 262 | }); |
| 263 | 263 | |
| 264 | −export const automationSnapshot = makeIpcMethod({ | |
| 264 | +export const automationSnapshot = DesktopIpc.makeIpcMethod({ | |
| 265 | 265 | channel: IpcChannels.PREVIEW_AUTOMATION_SNAPSHOT_CHANNEL, |
| 266 | 266 | payload: DesktopPreviewTabInputSchema, |
| 267 | 267 | result: PreviewAutomationSnapshot, |
| @@ -271,7 +271,7 @@ export const automationSnapshot = makeIpcMethod({ · 3 unmodified lines | ||
| 271 | 271 | }), |
| 272 | 272 | }); |
| 273 | 273 | |
| 274 | −export const automationClick = makeIpcMethod({ | |
| 274 | +export const automationClick = DesktopIpc.makeIpcMethod({ | |
| 275 | 275 | channel: IpcChannels.PREVIEW_AUTOMATION_CLICK_CHANNEL, |
| 276 | 276 | payload: DesktopPreviewAutomationClickInputSchema, |
| 277 | 277 | result: Schema.Void, |
| @@ -281,7 +281,7 @@ export const automationClick = makeIpcMethod({ · 3 unmodified lines | ||
| 281 | 281 | }), |
| 282 | 282 | }); |
| 283 | 283 | |
| 284 | −export const automationType = makeIpcMethod({ | |
| 284 | +export const automationType = DesktopIpc.makeIpcMethod({ | |
| 285 | 285 | channel: IpcChannels.PREVIEW_AUTOMATION_TYPE_CHANNEL, |
| 286 | 286 | payload: DesktopPreviewAutomationTypeInputSchema, |
| 287 | 287 | result: Schema.Void, |
| @@ -291,7 +291,7 @@ export const automationType = makeIpcMethod({ · 3 unmodified lines | ||
| 291 | 291 | }), |
| 292 | 292 | }); |
| 293 | 293 | |
| 294 | −export const automationPress = makeIpcMethod({ | |
| 294 | +export const automationPress = DesktopIpc.makeIpcMethod({ | |
| 295 | 295 | channel: IpcChannels.PREVIEW_AUTOMATION_PRESS_CHANNEL, |
| 296 | 296 | payload: DesktopPreviewAutomationPressInputSchema, |
| 297 | 297 | result: Schema.Void, |
| @@ -301,7 +301,7 @@ export const automationPress = makeIpcMethod({ · 3 unmodified lines | ||
| 301 | 301 | }), |
| 302 | 302 | }); |
| 303 | 303 | |
| 304 | −export const automationScroll = makeIpcMethod({ | |
| 304 | +export const automationScroll = DesktopIpc.makeIpcMethod({ | |
| 305 | 305 | channel: IpcChannels.PREVIEW_AUTOMATION_SCROLL_CHANNEL, |
| 306 | 306 | payload: DesktopPreviewAutomationScrollInputSchema, |
| 307 | 307 | result: Schema.Void, |
| @@ -311,7 +311,7 @@ export const automationScroll = makeIpcMethod({ · 3 unmodified lines | ||
| 311 | 311 | }), |
| 312 | 312 | }); |
| 313 | 313 | |
| 314 | −export const automationEvaluate = makeIpcMethod({ | |
| 314 | +export const automationEvaluate = DesktopIpc.makeIpcMethod({ | |
| 315 | 315 | channel: IpcChannels.PREVIEW_AUTOMATION_EVALUATE_CHANNEL, |
| 316 | 316 | payload: DesktopPreviewAutomationEvaluateInputSchema, |
| 317 | 317 | result: Schema.Unknown, |
| @@ -321,7 +321,7 @@ export const automationEvaluate = makeIpcMethod({ · 3 unmodified lines | ||
| 321 | 321 | }), |
| 322 | 322 | }); |
| 323 | 323 | |
| 324 | −export const automationWaitFor = makeIpcMethod({ | |
| 324 | +export const automationWaitFor = DesktopIpc.makeIpcMethod({ | |
| 325 | 325 | channel: IpcChannels.PREVIEW_AUTOMATION_WAIT_FOR_CHANNEL, |
| 326 | 326 | payload: DesktopPreviewAutomationWaitForInputSchema, |
| 327 | 327 | result: Schema.Void, |
| @@ -331,7 +331,7 @@ export const automationWaitFor = makeIpcMethod({ · 3 unmodified lines | ||
| 331 | 331 | }), |
| 332 | 332 | }); |
| 333 | 333 | |
| 334 | −export const saveRecording = makeIpcMethod({ | |
| 334 | +export const saveRecording = DesktopIpc.makeIpcMethod({ | |
| 335 | 335 | channel: IpcChannels.PREVIEW_RECORDING_SAVE_CHANNEL, |
| 336 | 336 | payload: DesktopPreviewRecordingSaveInputSchema, |
| 337 | 337 | result: DesktopPreviewRecordingArtifactSchema, |
| @@ -9,14 +9,14 @@ import * as Schema from "effect/Schema"; · 8 unmodified lines | ||
| 9 | 9 | import * as DesktopLifecycle from "../../app/DesktopLifecycle.ts"; |
| 10 | 10 | import * as DesktopServerExposure from "../../backend/DesktopServerExposure.ts"; |
| 11 | 11 | import * as IpcChannels from "../channels.ts"; |
| 12 | −import { makeIpcMethod } from "../DesktopIpc.ts"; | |
| 12 | +import * as DesktopIpc from "../DesktopIpc.ts"; | |
| 13 | 13 | |
| 14 | 14 | const SetTailscaleServeEnabledInput = Schema.Struct({ |
| 15 | 15 | enabled: Schema.Boolean, |
| 16 | 16 | port: Schema.optionalKey(Schema.Number), |
| 17 | 17 | }); |
| 18 | 18 | |
| 19 | −export const getServerExposureState = makeIpcMethod({ | |
| 19 | +export const getServerExposureState = DesktopIpc.makeIpcMethod({ | |
| 20 | 20 | channel: IpcChannels.GET_SERVER_EXPOSURE_STATE_CHANNEL, |
| 21 | 21 | payload: Schema.Void, |
| 22 | 22 | result: DesktopServerExposureStateSchema, |
| @@ -26,7 +26,7 @@ export const getServerExposureState = makeIpcMethod({ · 3 unmodified lines | ||
| 26 | 26 | }), |
| 27 | 27 | }); |
| 28 | 28 | |
| 29 | −export const setServerExposureMode = makeIpcMethod({ | |
| 29 | +export const setServerExposureMode = DesktopIpc.makeIpcMethod({ | |
| 30 | 30 | channel: IpcChannels.SET_SERVER_EXPOSURE_MODE_CHANNEL, |
| 31 | 31 | payload: DesktopServerExposureModeSchema, |
| 32 | 32 | result: DesktopServerExposureStateSchema, |
| @@ -41,7 +41,7 @@ export const setServerExposureMode = makeIpcMethod({ · 8 unmodified lines | ||
| 41 | 41 | }), |
| 42 | 42 | }); |
| 43 | 43 | |
| 44 | −export const setTailscaleServeEnabled = makeIpcMethod({ | |
| 44 | +export const setTailscaleServeEnabled = DesktopIpc.makeIpcMethod({ | |
| 45 | 45 | channel: IpcChannels.SET_TAILSCALE_SERVE_ENABLED_CHANNEL, |
| 46 | 46 | payload: SetTailscaleServeEnabledInput, |
| 47 | 47 | result: DesktopServerExposureStateSchema, |
| @@ -58,7 +58,7 @@ export const setTailscaleServeEnabled = makeIpcMethod({ · 10 unmodified lines | ||
| 58 | 58 | }), |
| 59 | 59 | }); |
| 60 | 60 | |
| 61 | −export const getAdvertisedEndpoints = makeIpcMethod({ | |
| 61 | +export const getAdvertisedEndpoints = DesktopIpc.makeIpcMethod({ | |
| 62 | 62 | channel: IpcChannels.GET_ADVERTISED_ENDPOINTS_CHANNEL, |
| 63 | 63 | payload: Schema.Void, |
| 64 | 64 | result: Schema.Array(AdvertisedEndpoint), |
| @@ -33,7 +33,7 @@ import * as Effect from "effect/Effect"; · 32 unmodified lines | ||
| 33 | 33 | import * as Schema from "effect/Schema"; |
| 34 | 34 | |
| 35 | 35 | import * as IpcChannels from "../channels.ts"; |
| 36 | −import { makeIpcMethod } from "../DesktopIpc.ts"; | |
| 36 | +import * as DesktopIpc from "../DesktopIpc.ts"; | |
| 37 | 37 | import * as DesktopSshEnvironment from "../../ssh/DesktopSshEnvironment.ts"; |
| 38 | 38 | import * as DesktopSshPasswordPrompts from "../../ssh/DesktopSshPasswordPrompts.ts"; |
| 39 | 39 | |
| @@ -107,7 +107,7 @@ const withLoopbackSshApi = · 67 unmodified lines | ||
| 107 | 107 | ), |
| 108 | 108 | ); |
| 109 | 109 | |
| 110 | −export const discoverSshHosts = makeIpcMethod({ | |
| 110 | +export const discoverSshHosts = DesktopIpc.makeIpcMethod({ | |
| 111 | 111 | channel: IpcChannels.DISCOVER_SSH_HOSTS_CHANNEL, |
| 112 | 112 | payload: Schema.Void, |
| 113 | 113 | result: Schema.Array(DesktopDiscoveredSshHostSchema), |
| @@ -117,7 +117,7 @@ export const discoverSshHosts = makeIpcMethod({ · 3 unmodified lines | ||
| 117 | 117 | }), |
| 118 | 118 | }); |
| 119 | 119 | |
| 120 | −export const ensureSshEnvironment = makeIpcMethod({ | |
| 120 | +export const ensureSshEnvironment = DesktopIpc.makeIpcMethod({ | |
| 121 | 121 | channel: IpcChannels.ENSURE_SSH_ENVIRONMENT_CHANNEL, |
| 122 | 122 | payload: DesktopSshEnvironmentEnsureInputSchema, |
| 123 | 123 | result: DesktopSshEnvironmentEnsureResultSchema, |
| @@ -139,7 +139,7 @@ export const ensureSshEnvironment = makeIpcMethod({ · 15 unmodified lines | ||
| 139 | 139 | }), |
| 140 | 140 | }); |
| 141 | 141 | |
| 142 | −export const disconnectSshEnvironment = makeIpcMethod({ | |
| 142 | +export const disconnectSshEnvironment = DesktopIpc.makeIpcMethod({ | |
| 143 | 143 | channel: IpcChannels.DISCONNECT_SSH_ENVIRONMENT_CHANNEL, |
| 144 | 144 | payload: DesktopSshEnvironmentTargetSchema, |
| 145 | 145 | result: Schema.Void, |
| @@ -149,7 +149,7 @@ export const disconnectSshEnvironment = makeIpcMethod({ · 3 unmodified lines | ||
| 149 | 149 | }), |
| 150 | 150 | }); |
| 151 | 151 | |
| 152 | −export const fetchSshEnvironmentDescriptor = makeIpcMethod({ | |
| 152 | +export const fetchSshEnvironmentDescriptor = DesktopIpc.makeIpcMethod({ | |
| 153 | 153 | channel: IpcChannels.FETCH_SSH_ENVIRONMENT_DESCRIPTOR_CHANNEL, |
| 154 | 154 | payload: DesktopSshHttpBaseUrlInputSchema, |
| 155 | 155 | result: ExecutionEnvironmentDescriptor, |
| @@ -160,7 +160,7 @@ export const fetchSshEnvironmentDescriptor = makeIpcMethod({ · 4 unmodified lines | ||
| 160 | 160 | }), |
| 161 | 161 | }); |
| 162 | 162 | |
| 163 | −export const bootstrapSshBearerSession = makeIpcMethod({ | |
| 163 | +export const bootstrapSshBearerSession = DesktopIpc.makeIpcMethod({ | |
| 164 | 164 | channel: IpcChannels.BOOTSTRAP_SSH_BEARER_SESSION_CHANNEL, |
| 165 | 165 | payload: DesktopSshBearerBootstrapInputSchema, |
| 166 | 166 | result: AuthAccessTokenResult, |
| @@ -177,7 +177,7 @@ export const bootstrapSshBearerSession = makeIpcMethod({ · 10 unmodified lines | ||
| 177 | 177 | }), |
| 178 | 178 | }); |
| 179 | 179 | |
| 180 | −export const fetchSshSessionState = makeIpcMethod({ | |
| 180 | +export const fetchSshSessionState = DesktopIpc.makeIpcMethod({ | |
| 181 | 181 | channel: IpcChannels.FETCH_SSH_SESSION_STATE_CHANNEL, |
| 182 | 182 | payload: DesktopSshBearerRequestInputSchema, |
| 183 | 183 | result: AuthSessionState, |
| @@ -194,7 +194,7 @@ export const fetchSshSessionState = makeIpcMethod({ · 10 unmodified lines | ||
| 194 | 194 | }), |
| 195 | 195 | }); |
| 196 | 196 | |
| 197 | −export const issueSshWebSocketTicket = makeIpcMethod({ | |
| 197 | +export const issueSshWebSocketTicket = DesktopIpc.makeIpcMethod({ | |
| 198 | 198 | channel: IpcChannels.ISSUE_SSH_WEBSOCKET_TOKEN_CHANNEL, |
| 199 | 199 | payload: DesktopSshBearerRequestInputSchema, |
| 200 | 200 | result: AuthWebSocketTicketResult, |
| @@ -211,7 +211,7 @@ export const issueSshWebSocketTicket = makeIpcMethod({ · 10 unmodified lines | ||
| 211 | 211 | }), |
| 212 | 212 | }); |
| 213 | 213 | |
| 214 | −export const resolveSshPasswordPrompt = makeIpcMethod({ | |
| 214 | +export const resolveSshPasswordPrompt = DesktopIpc.makeIpcMethod({ | |
| 215 | 215 | channel: IpcChannels.RESOLVE_SSH_PASSWORD_PROMPT_CHANNEL, |
| 216 | 216 | payload: DesktopSshPasswordPromptResolutionInputSchema, |
| 217 | 217 | result: Schema.Void, |
| @@ -9,9 +9,9 @@ import * as Schema from "effect/Schema"; · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | import * as DesktopUpdates from "../../updates/DesktopUpdates.ts"; |
| 11 | 11 | import * as IpcChannels from "../channels.ts"; |
| 12 | −import { makeIpcMethod } from "../DesktopIpc.ts"; | |
| 12 | +import * as DesktopIpc from "../DesktopIpc.ts"; | |
| 13 | 13 | |
| 14 | −export const getUpdateState = makeIpcMethod({ | |
| 14 | +export const getUpdateState = DesktopIpc.makeIpcMethod({ | |
| 15 | 15 | channel: IpcChannels.UPDATE_GET_STATE_CHANNEL, |
| 16 | 16 | payload: Schema.Void, |
| 17 | 17 | result: DesktopUpdateStateSchema, |
| @@ -21,7 +21,7 @@ export const getUpdateState = makeIpcMethod({ · 3 unmodified lines | ||
| 21 | 21 | }), |
| 22 | 22 | }); |
| 23 | 23 | |
| 24 | −export const setUpdateChannel = makeIpcMethod({ | |
| 24 | +export const setUpdateChannel = DesktopIpc.makeIpcMethod({ | |
| 25 | 25 | channel: IpcChannels.UPDATE_SET_CHANNEL_CHANNEL, |
| 26 | 26 | payload: DesktopUpdateChannelSchema, |
| 27 | 27 | result: DesktopUpdateStateSchema, |
| @@ -31,7 +31,7 @@ export const setUpdateChannel = makeIpcMethod({ · 3 unmodified lines | ||
| 31 | 31 | }), |
| 32 | 32 | }); |
| 33 | 33 | |
| 34 | −export const downloadUpdate = makeIpcMethod({ | |
| 34 | +export const downloadUpdate = DesktopIpc.makeIpcMethod({ | |
| 35 | 35 | channel: IpcChannels.UPDATE_DOWNLOAD_CHANNEL, |
| 36 | 36 | payload: Schema.Void, |
| 37 | 37 | result: DesktopUpdateActionResultSchema, |
| @@ -41,7 +41,7 @@ export const downloadUpdate = makeIpcMethod({ · 3 unmodified lines | ||
| 41 | 41 | }), |
| 42 | 42 | }); |
| 43 | 43 | |
| 44 | −export const installUpdate = makeIpcMethod({ | |
| 44 | +export const installUpdate = DesktopIpc.makeIpcMethod({ | |
| 45 | 45 | channel: IpcChannels.UPDATE_INSTALL_CHANNEL, |
| 46 | 46 | payload: Schema.Void, |
| 47 | 47 | result: DesktopUpdateActionResultSchema, |
| @@ -51,7 +51,7 @@ export const installUpdate = makeIpcMethod({ · 3 unmodified lines | ||
| 51 | 51 | }), |
| 52 | 52 | }); |
| 53 | 53 | |
| 54 | −export const checkForUpdate = makeIpcMethod({ | |
| 54 | +export const checkForUpdate = DesktopIpc.makeIpcMethod({ | |
| 55 | 55 | channel: IpcChannels.UPDATE_CHECK_CHANNEL, |
| 56 | 56 | payload: Schema.Void, |
| 57 | 57 | result: DesktopUpdateCheckResultSchema, |
| @@ -23,7 +23,7 @@ import * as ElectronShell from "../../electron/ElectronShell.ts"; · 22 unmodified lines | ||
| 23 | 23 | import * as ElectronTheme from "../../electron/ElectronTheme.ts"; |
| 24 | 24 | import * as ElectronWindow from "../../electron/ElectronWindow.ts"; |
| 25 | 25 | import * as IpcChannels from "../channels.ts"; |
| 26 | −import { makeIpcMethod, makeSyncIpcMethod } from "../DesktopIpc.ts"; | |
| 26 | +import * as DesktopIpc from "../DesktopIpc.ts"; | |
| 27 | 27 | import { |
| 28 | 28 | extractDistroFromUncPath, |
| 29 | 29 | resolveWslPickFolderDefaultPath, |
| @@ -46,7 +46,7 @@ function toWebSocketBaseUrl(httpBaseUrl: URL): string { · 16 unmodified lines | ||
| 46 | 46 | return url.href; |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | −export const getAppBranding = makeSyncIpcMethod({ | |
| 49 | +export const getAppBranding = DesktopIpc.makeSyncIpcMethod({ | |
| 50 | 50 | channel: IpcChannels.GET_APP_BRANDING_CHANNEL, |
| 51 | 51 | result: Schema.NullOr(DesktopAppBrandingSchema), |
| 52 | 52 | handler: Effect.fn("desktop.ipc.window.getAppBranding")(function* () { |
| @@ -55,7 +55,7 @@ export const getAppBranding = makeSyncIpcMethod({ · 2 unmodified lines | ||
| 55 | 55 | }), |
| 56 | 56 | }); |
| 57 | 57 | |
| 58 | −export const getLocalEnvironmentBootstraps = makeSyncIpcMethod({ | |
| 58 | +export const getLocalEnvironmentBootstraps = DesktopIpc.makeSyncIpcMethod({ | |
| 59 | 59 | channel: IpcChannels.GET_LOCAL_ENVIRONMENT_BOOTSTRAPS_CHANNEL, |
| 60 | 60 | result: Schema.Array(DesktopEnvironmentBootstrapSchema), |
| 61 | 61 | handler: Effect.fn("desktop.ipc.window.getLocalEnvironmentBootstraps")(function* () { |
| @@ -102,7 +102,7 @@ function extractWslDistroFromEnvironmentId(envId: string): string | null { · 40 unmodified lines | ||
| 102 | 102 | return suffix === "default" || suffix.length === 0 ? null : suffix; |
| 103 | 103 | } |
| 104 | 104 | |
| 105 | −export const getLocalEnvironmentBearerToken = makeIpcMethod({ | |
| 105 | +export const getLocalEnvironmentBearerToken = DesktopIpc.makeIpcMethod({ | |
| 106 | 106 | channel: IpcChannels.GET_LOCAL_ENVIRONMENT_BEARER_TOKEN_CHANNEL, |
| 107 | 107 | payload: Schema.Void, |
| 108 | 108 | result: Schema.String, |
| @@ -112,7 +112,7 @@ export const getLocalEnvironmentBearerToken = makeIpcMethod({ · 3 unmodified lines | ||
| 112 | 112 | }), |
| 113 | 113 | }); |
| 114 | 114 | |
| 115 | −export const pickFolder = makeIpcMethod({ | |
| 115 | +export const pickFolder = DesktopIpc.makeIpcMethod({ | |
| 116 | 116 | channel: IpcChannels.PICK_FOLDER_CHANNEL, |
| 117 | 117 | payload: Schema.UndefinedOr(PickFolderOptionsSchema), |
| 118 | 118 | result: Schema.NullOr(Schema.String), |
| @@ -179,7 +179,7 @@ export const pickFolder = makeIpcMethod({ · 60 unmodified lines | ||
| 179 | 179 | }), |
| 180 | 180 | }); |
| 181 | 181 | |
| 182 | −export const confirm = makeIpcMethod({ | |
| 182 | +export const confirm = DesktopIpc.makeIpcMethod({ | |
| 183 | 183 | channel: IpcChannels.CONFIRM_CHANNEL, |
| 184 | 184 | payload: Schema.String, |
| 185 | 185 | result: Schema.Boolean, |
| @@ -192,7 +192,7 @@ export const confirm = makeIpcMethod({ · 6 unmodified lines | ||
| 192 | 192 | }), |
| 193 | 193 | }); |
| 194 | 194 | |
| 195 | −export const setTheme = makeIpcMethod({ | |
| 195 | +export const setTheme = DesktopIpc.makeIpcMethod({ | |
| 196 | 196 | channel: IpcChannels.SET_THEME_CHANNEL, |
| 197 | 197 | payload: DesktopThemeSchema, |
| 198 | 198 | result: Schema.Void, |
| @@ -202,7 +202,7 @@ export const setTheme = makeIpcMethod({ · 3 unmodified lines | ||
| 202 | 202 | }), |
| 203 | 203 | }); |
| 204 | 204 | |
| 205 | −export const showContextMenu = makeIpcMethod({ | |
| 205 | +export const showContextMenu = DesktopIpc.makeIpcMethod({ | |
| 206 | 206 | channel: IpcChannels.CONTEXT_MENU_CHANNEL, |
| 207 | 207 | payload: ContextMenuInput, |
| 208 | 208 | result: Schema.NullOr(Schema.String), |
| @@ -223,7 +223,7 @@ export const showContextMenu = makeIpcMethod({ · 14 unmodified lines | ||
| 223 | 223 | }), |
| 224 | 224 | }); |
| 225 | 225 | |
| 226 | −export const openExternal = makeIpcMethod({ | |
| 226 | +export const openExternal = DesktopIpc.makeIpcMethod({ | |
| 227 | 227 | channel: IpcChannels.OPEN_EXTERNAL_CHANNEL, |
| 228 | 228 | payload: Schema.String, |
| 229 | 229 | result: Schema.Boolean, |
| @@ -49,7 +49,7 @@ const unusedLifecycleRuntimeLayer = Layer.mergeAll( · 48 unmodified lines | ||
| 49 | 49 | DesktopState.layer, |
| 50 | 50 | Layer.succeed( |
| 51 | 51 | DesktopEnvironment.DesktopEnvironment, |
| 52 | − DesktopEnvironment.DesktopEnvironment.of({} as DesktopEnvironment.DesktopEnvironmentShape), | |
| 52 | + DesktopEnvironment.DesktopEnvironment.of({} as DesktopEnvironment.DesktopEnvironment["Service"]), | |
| 53 | 53 | ), |
| 54 | 54 | Layer.succeed( |
| 55 | 55 | DesktopWindow.DesktopWindow, |
| @@ -14,7 +14,6 @@ import { resolveRemoteT3CliPackageSpec } from "@t3tools/ssh/command"; · 13 unmodified lines | ||
| 14 | 14 | import type { RemoteT3RunnerOptions } from "@t3tools/ssh/tunnel"; |
| 15 | 15 | import serverPackageJson from "../../server/package.json" with { type: "json" }; |
| 16 | 16 | |
| 17 | −import type { DesktopSettings as DesktopSettingsValue } from "./settings/DesktopAppSettings.ts"; | |
| 18 | 17 | import * as DesktopIpc from "./ipc/DesktopIpc.ts"; |
| 19 | 18 | import * as ElectronApp from "./electron/ElectronApp.ts"; |
| 20 | 19 | import * as ElectronDialog from "./electron/ElectronDialog.ts"; |
| @@ -70,8 +69,8 @@ const desktopEnvironmentLayer = Layer.unwrap( · 49 unmodified lines | ||
| 70 | 69 | ); |
| 71 | 70 | |
| 72 | 71 | const resolveDesktopSshCliRunner = ( |
| 73 | − environment: DesktopEnvironment.DesktopEnvironmentShape, | |
| 74 | − settings: DesktopSettingsValue, | |
| 72 | + environment: DesktopEnvironment.DesktopEnvironment["Service"], | |
| 73 | + settings: DesktopAppSettings.DesktopSettings, | |
| 75 | 74 | ): RemoteT3RunnerOptions => { |
| 76 | 75 | const devRemoteEntryPath = Option.getOrUndefined(environment.devRemoteT3ServerEntryPath); |
| 77 | 76 | if (environment.isDevelopment && devRemoteEntryPath !== undefined) { |
| @@ -112,7 +111,7 @@ const electronLayer = Layer.mergeAll( · 34 unmodified lines | ||
| 112 | 111 | ElectronTheme.layer, |
| 113 | 112 | ElectronUpdater.layer, |
| 114 | 113 | ElectronWindow.layer, |
| 115 | − Layer.succeed(DesktopIpc.DesktopIpc, DesktopIpc.make(Electron.ipcMain)), | |
| 114 | + DesktopIpc.layer(Electron.ipcMain), | |
| 116 | 115 | ); |
| 117 | 116 | |
| 118 | 117 | const desktopFoundationLayer = Layer.mergeAll( |
| @@ -59,7 +59,7 @@ const environmentLayer = Layer.succeed( · 58 unmodified lines | ||
| 59 | 59 | DesktopEnvironment.DesktopEnvironment, |
| 60 | 60 | DesktopEnvironment.DesktopEnvironment.of({ |
| 61 | 61 | browserArtifactsDir: "/tmp/t3/dev/browser-artifacts", |
| 62 | − } as DesktopEnvironment.DesktopEnvironmentShape), | |
| 62 | + } as DesktopEnvironment.DesktopEnvironment["Service"]), | |
| 63 | 63 | ); |
| 64 | 64 | |
| 65 | 65 | const fileSystemLayer = FileSystem.layerNoop({ |
| @@ -82,7 +82,7 @@ const layer = PreviewManager.layer.pipe( · 16 unmodified lines | ||
| 82 | 82 | |
| 83 | 83 | const withManager = <A>( |
| 84 | 84 | use: ( |
| 85 | − manager: PreviewManager.PreviewManagerShape, | |
| 85 | + manager: PreviewManager.PreviewManager["Service"], | |
| 86 | 86 | ) => Effect.Effect<A, PreviewManager.PreviewManagerError, Scope.Scope>, |
| 87 | 87 | ) => |
| 88 | 88 | Effect.gen(function* () { |
| @@ -64,7 +64,7 @@ function runShellEnvironment(input: { · 63 unmodified lines | ||
| 64 | 64 | DesktopEnvironment.DesktopEnvironment, |
| 65 | 65 | DesktopEnvironment.DesktopEnvironment.of({ |
| 66 | 66 | platform: input.platform, |
| 67 | − } as DesktopEnvironment.DesktopEnvironmentShape), | |
| 67 | + } as DesktopEnvironment.DesktopEnvironment["Service"]), | |
| 68 | 68 | ); |
| 69 | 69 | const spawnerLayer = Layer.succeed( |
| 70 | 70 | ChildProcessSpawner.ChildProcessSpawner, |
| @@ -127,7 +127,7 @@ function parseAppUpdateYml(raw: string): Effect.Effect<Option.Option<AppUpdateYm · 126 unmodified lines | ||
| 127 | 127 | function createBaseUpdateState( |
| 128 | 128 | channel: DesktopUpdateChannel, |
| 129 | 129 | enabled: boolean, |
| 130 | − environment: DesktopEnvironment.DesktopEnvironmentShape, | |
| 130 | + environment: DesktopEnvironment.DesktopEnvironment["Service"], | |
| 131 | 131 | ): DesktopUpdateState { |
| 132 | 132 | return { |
| 133 | 133 | ...createInitialDesktopUpdateState(environment.appVersion, environment.runtimeInfo, channel), |
| @@ -91,7 +91,7 @@ const desktopAssetsLayer = Layer.succeed(DesktopAssets.DesktopAssets, { · 90 unmodified lines | ||
| 91 | 91 | png: Option.none<string>(), |
| 92 | 92 | }), |
| 93 | 93 | resolveResourcePath: () => Effect.succeed(Option.none<string>()), |
| 94 | −} satisfies DesktopAssets.DesktopAssetsShape); | |
| 94 | +} satisfies DesktopAssets.DesktopAssets["Service"]); | |
| 95 | 95 | |
| 96 | 96 | const desktopServerExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExposure, { |
| 97 | 97 | getState: Effect.die("unexpected getState"), |
| @@ -106,19 +106,19 @@ const desktopServerExposureLayer = Layer.succeed(DesktopServerExposure.DesktopSe · 8 unmodified lines | ||
| 106 | 106 | setMode: () => Effect.die("unexpected setMode"), |
| 107 | 107 | setTailscaleServeEnabled: () => Effect.die("unexpected setTailscaleServeEnabled"), |
| 108 | 108 | getAdvertisedEndpoints: Effect.die("unexpected getAdvertisedEndpoints"), |
| 109 | −} satisfies DesktopServerExposure.DesktopServerExposureShape); | |
| 109 | +} satisfies DesktopServerExposure.DesktopServerExposure["Service"]); | |
| 110 | 110 | |
| 111 | 111 | const electronMenuLayer = Layer.succeed(ElectronMenu.ElectronMenu, { |
| 112 | 112 | setApplicationMenu: () => Effect.void, |
| 113 | 113 | popupTemplate: () => Effect.void, |
| 114 | 114 | showContextMenu: () => Effect.succeed(Option.none()), |
| 115 | −} satisfies ElectronMenu.ElectronMenuShape); | |
| 115 | +} satisfies ElectronMenu.ElectronMenu["Service"]); | |
| 116 | 116 | |
| 117 | 117 | const electronThemeLayer = Layer.succeed(ElectronTheme.ElectronTheme, { |
| 118 | 118 | shouldUseDarkColors: Effect.succeed(false), |
| 119 | 119 | setSource: () => Effect.void, |
| 120 | 120 | onUpdated: () => Effect.void, |
| 121 | −} satisfies ElectronTheme.ElectronThemeShape); | |
| 121 | +} satisfies ElectronTheme.ElectronTheme["Service"]); | |
| 122 | 122 | |
| 123 | 123 | const desktopEnvironmentLayer = DesktopEnvironment.layer(environmentInput).pipe( |
| 124 | 124 | Layer.provide( |
| @@ -156,7 +156,7 @@ function makeTestLayer(input: { · 31 unmodified lines | ||
| 156 | 156 | sendAll: () => Effect.void, |
| 157 | 157 | destroyAll: Effect.void, |
| 158 | 158 | syncAllAppearance: (sync) => sync(input.window), |
| 159 | − } satisfies ElectronWindow.ElectronWindowShape); | |
| 159 | + } satisfies ElectronWindow.ElectronWindow["Service"]); | |
| 160 | 160 | |
| 161 | 161 | return DesktopWindow.layer.pipe( |
| 162 | 162 | Layer.provide( |
| @@ -172,7 +172,7 @@ function makeTestLayer(input: { · 9 unmodified lines | ||
| 172 | 172 | return true; |
| 173 | 173 | }), |
| 174 | 174 | copyText: () => Effect.void, |
| 175 | − } satisfies ElectronShell.ElectronShellShape), | |
| 175 | + } satisfies ElectronShell.ElectronShell["Service"]), | |
| 176 | 176 | electronThemeLayer, |
| 177 | 177 | electronWindowLayer, |
| 178 | 178 | Layer.mock(PreviewManager.PreviewManager)({ |
| @@ -1,5 +1,6 @@ | ||
| 1 | 1 | included: |
| 2 | 2 | - ios/T3Code |
| 3 | + - modules/t3-composer-editor/ios | |
| 3 | 4 | - modules/t3-terminal/ios |
| 4 | 5 | - modules/t3-review-diff/ios |
| 5 | 6 | |
| @@ -192,11 +192,31 @@ · 191 unmodified lines | ||
| 192 | 192 | } |
| 193 | 193 | } |
| 194 | 194 | |
| 195 | −/* ─── Font family ───────────────────────────────────────────────────── */ | |
| 195 | +/* ─── Typography ────────────────────────────────────────────────────── */ | |
| 196 | 196 | @theme { |
| 197 | 197 | --font-sans: "DMSans_400Regular"; |
| 198 | 198 | --font-medium: "DMSans_500Medium"; |
| 199 | 199 | --font-bold: "DMSans_700Bold"; |
| 200 | + | |
| 201 | + /* Keep this scale aligned with src/lib/typography.ts for native style props. */ | |
| 202 | + --text-3xs: 10px; | |
| 203 | + --text-3xs--line-height: 13px; | |
| 204 | + --text-2xs: 11px; | |
| 205 | + --text-2xs--line-height: 15px; | |
| 206 | + --text-xs: 12px; | |
| 207 | + --text-xs--line-height: 16px; | |
| 208 | + --text-sm: 13px; | |
| 209 | + --text-sm--line-height: 18px; | |
| 210 | + --text-base: 15px; | |
| 211 | + --text-base--line-height: 22px; | |
| 212 | + --text-lg: 17px; | |
| 213 | + --text-lg--line-height: 22px; | |
| 214 | + --text-xl: 20px; | |
| 215 | + --text-xl--line-height: 26px; | |
| 216 | + --text-2xl: 24px; | |
| 217 | + --text-2xl--line-height: 30px; | |
| 218 | + --text-3xl: 28px; | |
| 219 | + --text-3xl--line-height: 34px; | |
| 200 | 220 | } |
| 201 | 221 | |
| 202 | 222 | /* ─── Custom utilities ──────────────────────────────────────────────── */ |
| @@ -275,8 +275,8 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { · 274 unmodified lines | ||
| 275 | 275 | fileTint: "#737373" |
| 276 | 276 | ) |
| 277 | 277 | private var fontFamily = "DMSans_400Regular" |
| 278 | − private var fontSize: CGFloat = 15 | |
| 279 | − private var lineHeight: CGFloat = 22 | |
| 278 | + private var fontSize: CGFloat = 14 | |
| 279 | + private var lineHeight: CGFloat = 20 | |
| 280 | 280 | private var contentInsetVertical: CGFloat = 0 |
| 281 | 281 | private var shouldAutoFocus = false |
| 282 | 282 | private var didAutoFocus = false |
| @@ -460,9 +460,19 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { · 177 unmodified lines | ||
| 460 | 460 | guard !isApplyingControlledValue else { |
| 461 | 461 | return |
| 462 | 462 | } |
| 463 | + restoreBaseTypingAttributes() | |
| 463 | 464 | emitSelection() |
| 464 | 465 | } |
| 465 | 466 | |
| 467 | + public func textView( | |
| 468 | + _ textView: UITextView, | |
| 469 | + shouldChangeTextIn range: NSRange, | |
| 470 | + replacementText text: String | |
| 471 | + ) -> Bool { | |
| 472 | + restoreBaseTypingAttributes() | |
| 473 | + return true | |
| 474 | + } | |
| 475 | + | |
| 466 | 476 | public func textViewDidBeginEditing(_ textView: UITextView) { |
| 467 | 477 | onComposerFocus() |
| 468 | 478 | } |
| @@ -484,6 +494,7 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { · 15 unmodified lines | ||
| 484 | 494 | let targetSelection = requestedSelection ?? previousSelection |
| 485 | 495 | requestedSelection = nil |
| 486 | 496 | textView.selectedRange = displayRange(for: targetSelection) |
| 497 | + restoreBaseTypingAttributes() | |
| 487 | 498 | isApplyingControlledValue = false |
| 488 | 499 | updatePlaceholderVisibility() |
| 489 | 500 | emitContentSizeIfNeeded() |
| @@ -556,7 +567,12 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { · 66 unmodified lines | ||
| 556 | 567 | size: image.size, |
| 557 | 568 | baselineOffset: baselineOffset |
| 558 | 569 | ) |
| 559 | − return NSAttributedString(attachment: attachment) | |
| 570 | + let attributedAttachment = NSMutableAttributedString(attachment: attachment) | |
| 571 | + attributedAttachment.addAttributes( | |
| 572 | + baseAttributes(), | |
| 573 | + range: NSRange(location: 0, length: attributedAttachment.length) | |
| 574 | + ) | |
| 575 | + return attributedAttachment | |
| 560 | 576 | } |
| 561 | 577 | |
| 562 | 578 | private func renderChip( |
| @@ -660,11 +676,18 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { · 97 unmodified lines | ||
| 660 | 676 | let font = UIFont(name: fontFamily, size: fontSize) |
| 661 | 677 | ?? UIFont.systemFont(ofSize: fontSize) |
| 662 | 678 | textView.font = font |
| 663 | − textView.typingAttributes = baseAttributes() | |
| 679 | + restoreBaseTypingAttributes() | |
| 664 | 680 | placeholderLabel.font = font |
| 665 | 681 | setNeedsLayout() |
| 666 | 682 | } |
| 667 | 683 | |
| 684 | + private func restoreBaseTypingAttributes() { | |
| 685 | + guard textView.markedTextRange == nil else { | |
| 686 | + return | |
| 687 | + } | |
| 688 | + textView.typingAttributes = baseAttributes() | |
| 689 | + } | |
| 690 | + | |
| 668 | 691 | private func applyTheme() { |
| 669 | 692 | textView.textColor = UIColor(composerHex: theme.text) ?? .label |
| 670 | 693 | placeholderLabel.textColor = UIColor(composerHex: theme.placeholder) ?? .placeholderText |
| @@ -21,7 +21,7 @@ export default function NotFoundRoute() { · 20 unmodified lines | ||
| 21 | 21 | }} |
| 22 | 22 | style={[{ flex: 1 }, screenBgStyle]} |
| 23 | 23 | > |
| 24 | − <Text className="text-[28px] font-t3-bold text-foreground" selectable> | |
| 24 | + <Text className="text-3xl font-t3-bold text-foreground" selectable> | |
| 25 | 25 | Route not found |
| 26 | 26 | </Text> |
| 27 | 27 | <Link href="/" asChild> |
| @@ -35,7 +35,7 @@ export default function NotFoundRoute() { · 7 unmodified lines | ||
| 35 | 35 | primaryBgStyle, |
| 36 | 36 | ]} |
| 37 | 37 | > |
| 38 | − <Text className="text-[16px] font-t3-bold text-primary-foreground">Return home</Text> | |
| 38 | + <Text className="text-base font-t3-bold text-primary-foreground">Return home</Text> | |
| 39 | 39 | </Pressable> |
| 40 | 40 | </Link> |
| 41 | 41 | </ScrollView> |
| @@ -85,7 +85,7 @@ export default function ConnectionsRouteScreen() { · 84 unmodified lines | ||
| 85 | 85 | type="monochrome" |
| 86 | 86 | /> |
| 87 | 87 | </View> |
| 88 | − <Text className="text-center text-[14px] leading-[20px] text-foreground-muted"> | |
| 88 | + <Text className="text-center text-sm leading-[20px] text-foreground-muted"> | |
| 89 | 89 | No environments connected yet.{"\n"}Tap{" "} |
| 90 | 90 | <Text className="font-t3-bold text-foreground">+</Text> to add one. |
| 91 | 91 | </Text> |
| @@ -171,7 +171,7 @@ export default function ConnectionsNewRouteScreen() { · 170 unmodified lines | ||
| 171 | 171 | className="items-center gap-3 rounded-[24px] bg-card px-5 py-8" |
| 172 | 172 | style={{ borderCurve: "continuous" }} |
| 173 | 173 | > |
| 174 | − <Text className="text-center text-[14px] leading-[20px] text-foreground-muted"> | |
| 174 | + <Text className="text-center text-sm leading-[20px] text-foreground-muted"> | |
| 175 | 175 | Camera permission is required to scan a QR code. |
| 176 | 176 | </Text> |
| 177 | 177 | <ConnectionSheetButton |
| @@ -189,7 +189,7 @@ export default function ConnectionsNewRouteScreen() { · 11 unmodified lines | ||
| 189 | 189 | <View collapsable={false} className="gap-4 rounded-[24px] bg-card p-4"> |
| 190 | 190 | <View collapsable={false} className="gap-1.5"> |
| 191 | 191 | <Text |
| 192 | − className="text-[11px] font-t3-bold uppercase text-foreground-muted" | |
| 192 | + className="text-2xs font-t3-bold uppercase text-foreground-muted" | |
| 193 | 193 | style={{ letterSpacing: 0.8 }} |
| 194 | 194 | > |
| 195 | 195 | Host |
| @@ -202,13 +202,13 @@ export default function ConnectionsNewRouteScreen() { · 6 unmodified lines | ||
| 202 | 202 | placeholderTextColor={placeholderColor} |
| 203 | 203 | value={hostInput} |
| 204 | 204 | onChangeText={handleHostChange} |
| 205 | − className="rounded-[14px] border border-input-border bg-input px-4 py-3.5 text-[15px] text-foreground" | |
| 205 | + className="rounded-[14px] border border-input-border bg-input px-4 py-3.5 text-base text-foreground" | |
| 206 | 206 | /> |
| 207 | 207 | </View> |
| 208 | 208 | |
| 209 | 209 | <View collapsable={false} className="gap-1.5"> |
| 210 | 210 | <Text |
| 211 | − className="text-[11px] font-t3-bold uppercase text-foreground-muted" | |
| 211 | + className="text-2xs font-t3-bold uppercase text-foreground-muted" | |
| 212 | 212 | style={{ letterSpacing: 0.8 }} |
| 213 | 213 | > |
| 214 | 214 | Pairing code |
| @@ -220,7 +220,7 @@ export default function ConnectionsNewRouteScreen() { · 5 unmodified lines | ||
| 220 | 220 | placeholderTextColor={placeholderColor} |
| 221 | 221 | value={codeInput} |
| 222 | 222 | onChangeText={handleCodeChange} |
| 223 | − className="rounded-[14px] border border-input-border bg-input px-4 py-3.5 text-[15px] text-foreground" | |
| 223 | + className="rounded-[14px] border border-input-border bg-input px-4 py-3.5 text-base text-foreground" | |
| 224 | 224 | /> |
| 225 | 225 | </View> |
| 226 | 226 | |
| @@ -129,10 +129,10 @@ export default function NewTaskRoute() { · 128 unmodified lines | ||
| 129 | 129 | {items.length === 0 ? ( |
| 130 | 130 | <View collapsable={false} className="items-center gap-3 rounded-[24px] bg-card px-6 py-8"> |
| 131 | 131 | {projectEmptyState.loading ? <ActivityIndicator color={accentColor} /> : null} |
| 132 | − <Text className="text-center text-[17px] font-t3-bold text-foreground"> | |
| 132 | + <Text className="text-center text-lg font-t3-bold text-foreground"> | |
| 133 | 133 | {projectEmptyState.title} |
| 134 | 134 | </Text> |
| 135 | − <Text className="text-center text-[14px] leading-[20px] text-foreground-muted"> | |
| 135 | + <Text className="text-center text-sm leading-[20px] text-foreground-muted"> | |
| 136 | 136 | {projectEmptyState.detail} |
| 137 | 137 | </Text> |
| 138 | 138 | {!catalogState.hasReadyEnvironment ? ( |
| @@ -140,7 +140,7 @@ export default function NewTaskRoute() { · 1 unmodified line | ||
| 140 | 140 | className="mt-1 rounded-full bg-primary px-4 py-2.5 active:opacity-70" |
| 141 | 141 | onPress={() => router.push("/connections/new")} |
| 142 | 142 | > |
| 143 | − <Text className="text-[13px] font-t3-bold text-primary-foreground"> | |
| 143 | + <Text className="text-sm font-t3-bold text-primary-foreground"> | |
| 144 | 144 | Add environment |
| 145 | 145 | </Text> |
| 146 | 146 | </Pressable> |
| @@ -149,7 +149,7 @@ export default function NewTaskRoute() { · 2 unmodified lines | ||
| 149 | 149 | className="mt-1 rounded-full bg-primary px-4 py-2.5 active:opacity-70" |
| 150 | 150 | onPress={() => router.push("/new/add-project")} |
| 151 | 151 | > |
| 152 | − <Text className="text-[13px] font-t3-bold text-primary-foreground"> | |
| 152 | + <Text className="text-sm font-t3-bold text-primary-foreground"> | |
| 153 | 153 | Add new project |
| 154 | 154 | </Text> |
| 155 | 155 | </Pressable> |
| @@ -197,9 +197,7 @@ export default function NewTaskRoute() { · 41 unmodified lines | ||
| 197 | 197 | /> |
| 198 | 198 | </View> |
| 199 | 199 | <View className="flex-1"> |
| 200 | − <Text className="text-[16px] leading-[21px] font-t3-bold"> | |
| 201 | − {item.title} | |
| 202 | − </Text> | |
| 200 | + <Text className="text-base leading-[21px] font-t3-bold">{item.title}</Text> | |
| 203 | 201 | </View> |
| 204 | 202 | <SymbolView |
| 205 | 203 | name="chevron.right" |
| @@ -111,7 +111,7 @@ export default function SettingsEnvironmentsRouteScreen() { · 110 unmodified lines | ||
| 111 | 111 | type="monochrome" |
| 112 | 112 | /> |
| 113 | 113 | </View> |
| 114 | − <Text className="text-center text-[14px] leading-[20px] text-foreground-muted"> | |
| 114 | + <Text className="text-center text-sm leading-[20px] text-foreground-muted"> | |
| 115 | 115 | No environments connected yet.{"\n"}Tap{" "} |
| 116 | 116 | <Text className="font-t3-bold text-foreground">+</Text> to add one. |
| 117 | 117 | </Text> |
| @@ -160,7 +160,7 @@ function ConfiguredCloudEnvironmentRows(props: { · 42 unmodified lines | ||
| 160 | 160 | return ( |
| 161 | 161 | <View collapsable={false} className="mt-5 gap-3"> |
| 162 | 162 | <View className="flex-row items-center justify-between px-1"> |
| 163 | − <Text className="text-[13px] font-t3-bold uppercase text-foreground-muted">T3 Cloud</Text> | |
| 163 | + <Text className="text-sm font-t3-bold uppercase text-foreground-muted">T3 Cloud</Text> | |
| 164 | 164 | <Pressable |
| 165 | 165 | accessibilityRole="button" |
| 166 | 166 | disabled={controller.relayDiscovery.isRefreshing} |
| @@ -204,16 +204,16 @@ function ConfiguredCloudEnvironmentRows(props: { · 37 unmodified lines | ||
| 204 | 204 | ) : controller.relayDiscovery.isRefreshing ? ( |
| 205 | 205 | <View collapsable={false} className="items-center gap-3 rounded-[24px] bg-card p-6"> |
| 206 | 206 | <ActivityIndicator color={iconColor} /> |
| 207 | − <Text className="text-center text-[14px] leading-[20px] text-foreground-muted"> | |
| 207 | + <Text className="text-center text-sm leading-[20px] text-foreground-muted"> | |
| 208 | 208 | Loading linked cloud environments. |
| 209 | 209 | </Text> |
| 210 | 210 | </View> |
| 211 | 211 | ) : controller.relayDiscovery.error ? ( |
| 212 | 212 | <View collapsable={false} className="gap-3 rounded-[24px] bg-card p-5"> |
| 213 | − <Text className="text-[15px] font-t3-bold text-foreground"> | |
| 213 | + <Text className="text-base font-t3-bold text-foreground"> | |
| 214 | 214 | Could not load T3 Cloud environments |
| 215 | 215 | </Text> |
| 216 | − <Text className="text-[13px] leading-[18px] text-foreground-muted"> | |
| 216 | + <Text className="text-sm leading-[18px] text-foreground-muted"> | |
| 217 | 217 | {controller.relayDiscovery.error} |
| 218 | 218 | </Text> |
| 219 | 219 | {controller.relayDiscovery.errorTraceId ? ( |
| @@ -222,7 +222,7 @@ function ConfiguredCloudEnvironmentRows(props: { · 2 unmodified lines | ||
| 222 | 222 | </View> |
| 223 | 223 | ) : ( |
| 224 | 224 | <View collapsable={false} className="rounded-[24px] bg-card p-5"> |
| 225 | − <Text className="text-[14px] leading-[20px] text-foreground-muted"> | |
| 225 | + <Text className="text-sm leading-[20px] text-foreground-muted"> | |
| 226 | 226 | No additional linked cloud environments. |
| 227 | 227 | </Text> |
| 228 | 228 | </View> |
| @@ -361,7 +361,7 @@ function CloudEnvironmentRowShell(props: { · 132 unmodified lines | ||
| 361 | 361 | <View className="min-w-0 flex-row items-center gap-2"> |
| 362 | 362 | <ConnectionStatusDot state={props.connectionState} pulse={shouldPulse} size={7} /> |
| 363 | 363 | <Text |
| 364 | − className="min-w-0 flex-shrink text-[16px] font-t3-bold leading-[21px] text-foreground" | |
| 364 | + className="min-w-0 flex-shrink text-base font-t3-bold leading-[21px] text-foreground" | |
| 365 | 365 | numberOfLines={1} |
| 366 | 366 | > |
| 367 | 367 | {props.label} |
| @@ -370,7 +370,7 @@ function CloudEnvironmentRowShell(props: { · 2 unmodified lines | ||
| 370 | 370 | {props.connectionError ? ( |
| 371 | 371 | <Text |
| 372 | 372 | aria-hidden |
| 373 | − className={cn("absolute left-0 right-0 text-[12px] leading-[16px]", statusClassName)} | |
| 373 | + className={cn("absolute left-0 right-0 text-xs leading-[16px]", statusClassName)} | |
| 374 | 374 | onTextLayout={onMeasuredErrorTextLayout} |
| 375 | 375 | style={{ opacity: 0, zIndex: -1 }} |
| 376 | 376 | > |
| @@ -384,7 +384,7 @@ function CloudEnvironmentRowShell(props: { · 7 unmodified lines | ||
| 384 | 384 | className="min-w-0 flex-row items-start gap-1" |
| 385 | 385 | > |
| 386 | 386 | <Text |
| 387 | − className={cn("min-w-0 flex-1 text-[12px] leading-[16px]", statusClassName)} | |
| 387 | + className={cn("min-w-0 flex-1 text-xs leading-[16px]", statusClassName)} | |
| 388 | 388 | numberOfLines={isErrorExpanded ? undefined : 1} |
| 389 | 389 | > |
| 390 | 390 | {statusText} |
| @@ -394,7 +394,7 @@ function CloudEnvironmentRowShell(props: { · 3 unmodified lines | ||
| 394 | 394 | <Text |
| 395 | 395 | accessibilityHint="Copies the trace ID" |
| 396 | 396 | accessibilityRole="button" |
| 397 | − className={cn("text-[12px] leading-[16px] underline", statusClassName)} | |
| 397 | + className={cn("text-xs leading-[16px] underline", statusClassName)} | |
| 398 | 398 | onLongPress={(event) => { |
| 399 | 399 | event.stopPropagation(); |
| 400 | 400 | copyTextWithHaptic(errorTraceId); |
| @@ -446,7 +446,7 @@ function CopyTraceIdButton(props: { readonly traceId: string }) { · 45 unmodified lines | ||
| 446 | 446 | className="self-start flex-row items-center gap-1.5 rounded-full bg-subtle px-3 py-2 active:opacity-70" |
| 447 | 447 | > |
| 448 | 448 | <SymbolView name="doc.on.doc" size={12} tintColor={iconColor} type="monochrome" /> |
| 449 | − <Text className="text-[12px] font-t3-bold text-foreground">Copy trace ID</Text> | |
| 449 | + <Text className="text-xs font-t3-bold text-foreground">Copy trace ID</Text> | |
| 450 | 450 | </Pressable> |
| 451 | 451 | ); |
| 452 | 452 | } |
| @@ -346,7 +346,7 @@ function ConfiguredSettingsRouteScreen() { · 345 unmodified lines | ||
| 346 | 346 | onPress={openAccount} |
| 347 | 347 | /> |
| 348 | 348 | </SettingsSection> |
| 349 | − <Text className="px-2 text-[13px] leading-[18px] text-foreground-muted"> | |
| 349 | + <Text className="px-2 text-sm leading-[18px] text-foreground-muted"> | |
| 350 | 350 | T3 Code works locally without signing in. Cloud features are optional. |
| 351 | 351 | </Text> |
| 352 | 352 | </View> |
| @@ -389,7 +389,7 @@ type SymbolName = ComponentProps<typeof SymbolView>["name"]; · 36 unmodified lines | ||
| 389 | 389 | function SettingsSection(props: { readonly title: string; readonly children: ReactNode }) { |
| 390 | 390 | return ( |
| 391 | 391 | <View className="gap-2"> |
| 392 | − <Text className="px-2 text-[13px] font-t3-medium text-foreground-muted">{props.title}</Text> | |
| 392 | + <Text className="px-2 text-sm font-t3-medium text-foreground-muted">{props.title}</Text> | |
| 393 | 393 | <View |
| 394 | 394 | className="overflow-hidden rounded-[28px] bg-card" |
| 395 | 395 | style={{ borderCurve: "continuous" }} |
| @@ -413,8 +413,8 @@ function AppSettingsSection() { · 17 unmodified lines | ||
| 413 | 413 | type="monochrome" |
| 414 | 414 | weight="regular" |
| 415 | 415 | /> |
| 416 | − <Text className="flex-1 text-[17px] text-foreground">Version</Text> | |
| 417 | − <Text className="text-[17px] text-foreground-muted">Alpha</Text> | |
| 416 | + <Text className="flex-1 text-lg text-foreground">Version</Text> | |
| 417 | + <Text className="text-lg text-foreground-muted">Alpha</Text> | |
| 418 | 418 | </View> |
| 419 | 419 | </SettingsSection> |
| 420 | 420 | ); |
| @@ -444,13 +444,13 @@ function SettingsRow(props: { · 23 unmodified lines | ||
| 444 | 444 | style={{ opacity: props.disabled ? 0.45 : 1 }} |
| 445 | 445 | > |
| 446 | 446 | <SymbolView name={props.icon} size={22} tintColor={icon} type="monochrome" weight="regular" /> |
| 447 | − <Text className="shrink-0 text-[17px] text-foreground" numberOfLines={1}> | |
| 447 | + <Text className="shrink-0 text-lg text-foreground" numberOfLines={1}> | |
| 448 | 448 | {props.label} |
| 449 | 449 | </Text> |
| 450 | 450 | <View className="min-w-0 flex-1 items-end"> |
| 451 | 451 | {props.value ? ( |
| 452 | 452 | <Text |
| 453 | − className="max-w-[180px] text-right text-[16px] text-foreground-muted" | |
| 453 | + className="max-w-[180px] text-right text-base text-foreground-muted" | |
| 454 | 454 | ellipsizeMode="middle" |
| 455 | 455 | numberOfLines={1} |
| 456 | 456 | > |
| @@ -502,7 +502,7 @@ function SettingsSwitchRow(props: { · 45 unmodified lines | ||
| 502 | 502 | style={{ opacity: props.disabled ? 0.45 : 1 }} |
| 503 | 503 | > |
| 504 | 504 | <SymbolView name={props.icon} size={22} tintColor={icon} type="monochrome" weight="regular" /> |
| 505 | − <Text className="flex-1 text-[17px] text-foreground">{props.label}</Text> | |
| 505 | + <Text className="flex-1 text-lg text-foreground">{props.label}</Text> | |
| 506 | 506 | <Switch |
| 507 | 507 | disabled={props.disabled} |
| 508 | 508 | ios_backgroundColor={track} |
| @@ -40,7 +40,7 @@ export function AppTextInput({ · 39 unmodified lines | ||
| 40 | 40 | <RNTextInput |
| 41 | 41 | ref={ref} |
| 42 | 42 | className={cn( |
| 43 | − "min-h-[54px] rounded-2xl border border-input-border bg-input px-3.5 py-3 font-sans text-[15px] text-foreground", | |
| 43 | + "min-h-[54px] rounded-2xl border border-input-border bg-input px-3.5 py-3 font-sans text-base text-foreground", | |
| 44 | 44 | className, |
| 45 | 45 | )} |
| 46 | 46 | placeholderTextColor={placeholderTextColor ?? placeholderColor} |
| @@ -22,15 +22,12 @@ export function BrandMark(props: { readonly compact?: boolean; readonly stageLab · 21 unmodified lines | ||
| 22 | 22 | /> |
| 23 | 23 | <View className="gap-1"> |
| 24 | 24 | <View className="flex-row items-center gap-2"> |
| 25 | − <Text | |
| 26 | − className="text-[17px] font-t3-bold text-foreground" | |
| 27 | − style={{ letterSpacing: -0.4 }} | |
| 28 | − > | |
| 25 | + <Text className="text-lg font-t3-bold text-foreground" style={{ letterSpacing: -0.4 }}> | |
| 29 | 26 | T3 Code |
| 30 | 27 | </Text> |
| 31 | 28 | <View className="rounded-full bg-subtle px-2 py-1"> |
| 32 | 29 | <Text |
| 33 | − className="text-[10px] font-t3-bold uppercase text-foreground-muted" | |
| 30 | + className="text-3xs font-t3-bold uppercase text-foreground-muted" | |
| 34 | 31 | style={{ letterSpacing: 1.1 }} |
| 35 | 32 | > |
| 36 | 33 | {stageLabel} |
| @@ -38,7 +35,7 @@ export function BrandMark(props: { readonly compact?: boolean; readonly stageLab · 1 unmodified line | ||
| 38 | 35 | </View> |
| 39 | 36 | </View> |
| 40 | 37 | {!compact ? ( |
| 41 | − <Text className="text-[12px] font-medium text-foreground-muted"> | |
| 38 | + <Text className="text-xs font-medium text-foreground-muted"> | |
| 42 | 39 | Mobile control surface for your live coding environments |
| 43 | 40 | </Text> |
| 44 | 41 | ) : null} |
| @@ -223,7 +223,7 @@ export function ComposerToolbarButton(props: { · 222 unmodified lines | ||
| 223 | 223 | {props.label ? ( |
| 224 | 224 | <Text |
| 225 | 225 | className={cn( |
| 226 | − "shrink text-center text-[13px] font-t3-bold", | |
| 226 | + "shrink text-center text-sm font-t3-bold", | |
| 227 | 227 | variant === "primary" |
| 228 | 228 | ? props.disabled |
| 229 | 229 | ? "text-foreground-muted" |
| @@ -48,7 +48,7 @@ export function ControlPill(props: { · 47 unmodified lines | ||
| 48 | 48 | : "bg-subtle", |
| 49 | 49 | ); |
| 50 | 50 | const labelClassName = cn( |
| 51 | − "text-center text-[12px] font-t3-bold", | |
| 51 | + "text-center text-xs font-t3-bold", | |
| 52 | 52 | variant === "primary" |
| 53 | 53 | ? props.disabled |
| 54 | 54 | ? "text-foreground-muted" |
| @@ -19,9 +19,7 @@ export function EmptyState(props: { · 18 unmodified lines | ||
| 19 | 19 | className="mt-4 self-start rounded-full bg-primary px-4 py-2.5 active:opacity-70" |
| 20 | 20 | onPress={props.onAction} |
| 21 | 21 | > |
| 22 | − <Text className="text-[13px] font-t3-bold text-primary-foreground"> | |
| 23 | − {props.actionLabel} | |
| 24 | − </Text> | |
| 22 | + <Text className="text-sm font-t3-bold text-primary-foreground">{props.actionLabel}</Text> | |
| 25 | 23 | </Pressable> |
| 26 | 24 | ) : null} |
| 27 | 25 | </View> |
| @@ -4,7 +4,7 @@ import { AppText as Text } from "./AppText"; · 3 unmodified lines | ||
| 4 | 4 | export function ErrorBanner(props: { readonly message: string }) { |
| 5 | 5 | return ( |
| 6 | 6 | <View className="rounded-2xl border border-rose-300/70 bg-rose-100/80 px-3.5 py-3 dark:border-rose-400/28 dark:bg-rose-500/12"> |
| 7 | − <Text className="font-t3-medium text-[13px] leading-[18px] text-rose-700 dark:text-rose-300"> | |
| 7 | + <Text className="font-t3-medium text-sm leading-[18px] text-rose-700 dark:text-rose-300"> | |
| 8 | 8 | {props.message} |
| 9 | 9 | </Text> |
| 10 | 10 | </View> |
| @@ -26,7 +26,7 @@ export function StatusPill( · 25 unmodified lines | ||
| 26 | 26 | <Text |
| 27 | 27 | className={cn( |
| 28 | 28 | "font-t3-bold", |
| 29 | − size === "compact" ? "text-[11px]" : "text-xs", | |
| 29 | + size === "compact" ? "text-2xs" : "text-xs", | |
| 30 | 30 | props.textClassName, |
| 31 | 31 | )} |
| 32 | 32 | > |
| @@ -147,14 +147,14 @@ function ProjectGroupLabel(props: { · 146 unmodified lines | ||
| 147 | 147 | workspaceRoot={props.project.workspaceRoot} |
| 148 | 148 | /> |
| 149 | 149 | <Text |
| 150 | − className="flex-1 text-[12px] font-t3-medium uppercase text-foreground-muted" | |
| 150 | + className="flex-1 text-xs font-t3-medium uppercase text-foreground-muted" | |
| 151 | 151 | numberOfLines={1} |
| 152 | 152 | style={{ letterSpacing: 0.5 }} |
| 153 | 153 | > |
| 154 | 154 | {props.project.title} |
| 155 | 155 | </Text> |
| 156 | 156 | {props.environmentLabel ? ( |
| 157 | − <Text className="max-w-[42%] text-[11px] text-foreground-tertiary" numberOfLines={1}> | |
| 157 | + <Text className="max-w-[42%] text-2xs text-foreground-tertiary" numberOfLines={1}> | |
| 158 | 158 | {props.environmentLabel} |
| 159 | 159 | </Text> |
| 160 | 160 | ) : null} |
| @@ -265,13 +265,13 @@ function ArchivedThreadRow(props: { · 104 unmodified lines | ||
| 265 | 265 | <View className="min-w-0 flex-1 gap-1"> |
| 266 | 266 | <View className="flex-row items-center gap-2"> |
| 267 | 267 | <Text |
| 268 | − className="min-w-0 flex-1 text-[15px] font-t3-bold leading-[20px] text-foreground" | |
| 268 | + className="min-w-0 flex-1 text-base font-t3-bold leading-[20px] text-foreground" | |
| 269 | 269 | numberOfLines={1} |
| 270 | 270 | > |
| 271 | 271 | {props.thread.title} |
| 272 | 272 | </Text> |
| 273 | 273 | <Text |
| 274 | − className="text-[12px] text-foreground-tertiary" | |
| 274 | + className="text-xs text-foreground-tertiary" | |
| 275 | 275 | style={{ fontVariant: ["tabular-nums"] }} |
| 276 | 276 | > |
| 277 | 277 | {timestamp} |
| @@ -286,7 +286,7 @@ function ArchivedThreadRow(props: { · 8 unmodified lines | ||
| 286 | 286 | type="monochrome" |
| 287 | 287 | /> |
| 288 | 288 | <Text |
| 289 | − className="min-w-0 flex-1 text-[11px] text-foreground-tertiary" | |
| 289 | + className="min-w-0 flex-1 text-2xs text-foreground-tertiary" | |
| 290 | 290 | numberOfLines={1} |
| 291 | 291 | style={{ fontFamily: "monospace" }} |
| 292 | 292 | > |
| @@ -314,10 +314,12 @@ function ArchivedThreadRow(props: { · 21 unmodified lines | ||
| 314 | 314 | function ArchiveError(props: { readonly message: string; readonly onRetry: () => void }) { |
| 315 | 315 | return ( |
| 316 | 316 | <View className="rounded-[20px] border border-danger-border bg-danger p-4"> |
| 317 | − <Text className="font-t3-bold text-danger-foreground">Could not load every archive</Text> | |
| 318 | − <Text className="mt-1 text-[13px] leading-[18px] text-foreground-muted">{props.message}</Text> | |
| 317 | + <Text className="text-base font-t3-bold text-danger-foreground"> | |
| 318 | + Could not load every archive | |
| 319 | + </Text> | |
| 320 | + <Text className="mt-1 text-sm leading-[18px] text-foreground-muted">{props.message}</Text> | |
| 319 | 321 | <Pressable className="mt-3 self-start active:opacity-60" onPress={props.onRetry}> |
| 320 | − <Text className="text-[13px] font-t3-bold text-danger-foreground">Try again</Text> | |
| 322 | + <Text className="text-sm font-t3-bold text-danger-foreground">Try again</Text> | |
| 321 | 323 | </Pressable> |
| 322 | 324 | </View> |
| 323 | 325 | ); |
| @@ -386,7 +388,7 @@ export function ArchivedThreadsScreen(props: { · 62 unmodified lines | ||
| 386 | 388 | {isInitialLoad ? ( |
| 387 | 389 | <View className="items-center py-16"> |
| 388 | 390 | <ActivityIndicator color={refreshTint} /> |
| 389 | − <Text className="mt-3 text-[13px] text-foreground-muted">Loading archive…</Text> | |
| 391 | + <Text className="mt-3 text-sm text-foreground-muted">Loading archive…</Text> | |
| 390 | 392 | </View> |
| 391 | 393 | ) : props.groups.length === 0 ? ( |
| 392 | 394 | <EmptyState |
| @@ -2,6 +2,7 @@ import { useWaitlist } from "@clerk/expo"; · 1 unmodified line | ||
| 2 | 2 | import { ActivityIndicator, Pressable, StyleSheet, Text, TextInput, View } from "react-native"; |
| 3 | 3 | import { useState } from "react"; |
| 4 | 4 | |
| 5 | +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 5 | 6 | import { useThemeColor } from "../../lib/useThemeColor"; |
| 6 | 7 | |
| 7 | 8 | export function CloudWaitlistEnrollment(props: { readonly onSignIn: () => void }) { |
| @@ -141,12 +142,11 @@ function useCloudWaitlistColors() { · 133 unmodified lines | ||
| 141 | 142 | const styles = StyleSheet.create({ |
| 142 | 143 | body: { |
| 143 | 144 | fontFamily: "DMSans_400Regular", |
| 144 | − fontSize: 15, | |
| 145 | − lineHeight: 21, | |
| 145 | + ...MOBILE_TYPOGRAPHY.body, | |
| 146 | 146 | }, |
| 147 | 147 | buttonText: { |
| 148 | 148 | fontFamily: "DMSans_700Bold", |
| 149 | − fontSize: 16, | |
| 149 | + fontSize: MOBILE_TYPOGRAPHY.body.fontSize, | |
| 150 | 150 | }, |
| 151 | 151 | content: { |
| 152 | 152 | gap: 18, |
| @@ -156,8 +156,7 @@ const styles = StyleSheet.create({ · 3 unmodified lines | ||
| 156 | 156 | }, |
| 157 | 157 | error: { |
| 158 | 158 | fontFamily: "DMSans_400Regular", |
| 159 | − fontSize: 13, | |
| 160 | − lineHeight: 18, | |
| 159 | + ...MOBILE_TYPOGRAPHY.footnote, | |
| 161 | 160 | }, |
| 162 | 161 | field: { |
| 163 | 162 | gap: 8, |
| @@ -167,15 +166,14 @@ const styles = StyleSheet.create({ · 3 unmodified lines | ||
| 167 | 166 | borderRadius: 16, |
| 168 | 167 | borderWidth: 1, |
| 169 | 168 | fontFamily: "DMSans_400Regular", |
| 170 | − fontSize: 17, | |
| 169 | + fontSize: MOBILE_TYPOGRAPHY.headline.fontSize, | |
| 171 | 170 | minHeight: 54, |
| 172 | 171 | paddingHorizontal: 16, |
| 173 | 172 | paddingVertical: 14, |
| 174 | 173 | }, |
| 175 | 174 | label: { |
| 176 | 175 | fontFamily: "DMSans_700Bold", |
| 177 | − fontSize: 13, | |
| 178 | − lineHeight: 18, | |
| 176 | + ...MOBILE_TYPOGRAPHY.footnote, | |
| 179 | 177 | }, |
| 180 | 178 | primaryButton: { |
| 181 | 179 | alignItems: "center", |
| @@ -196,13 +194,11 @@ const styles = StyleSheet.create({ · 14 unmodified lines | ||
| 196 | 194 | }, |
| 197 | 195 | signInText: { |
| 198 | 196 | fontFamily: "DMSans_700Bold", |
| 199 | − fontSize: 15, | |
| 200 | − lineHeight: 21, | |
| 197 | + ...MOBILE_TYPOGRAPHY.body, | |
| 201 | 198 | }, |
| 202 | 199 | title: { |
| 203 | 200 | fontFamily: "DMSans_700Bold", |
| 204 | − fontSize: 20, | |
| 205 | − lineHeight: 26, | |
| 201 | + ...MOBILE_TYPOGRAPHY.title, | |
| 206 | 202 | textAlign: "center", |
| 207 | 203 | }, |
| 208 | 204 | }); |
| @@ -76,19 +76,16 @@ export function ConnectionEnvironmentRow(props: { · 75 unmodified lines | ||
| 76 | 76 | /> |
| 77 | 77 | |
| 78 | 78 | <View className="flex-1 gap-0.5"> |
| 79 | − <Text | |
| 80 | − className="text-[16px] font-t3-bold leading-[21px] text-foreground" | |
| 81 | − numberOfLines={1} | |
| 82 | − > | |
| 79 | + <Text className="text-base font-t3-bold leading-[21px] text-foreground" numberOfLines={1}> | |
| 83 | 80 | {props.environment.environmentLabel} |
| 84 | 81 | </Text> |
| 85 | − <Text className="text-[12px] leading-[16px] text-foreground-muted" numberOfLines={1}> | |
| 82 | + <Text className="text-xs leading-[16px] text-foreground-muted" numberOfLines={1}> | |
| 86 | 83 | {props.environment.displayUrl} |
| 87 | 84 | </Text> |
| 88 | 85 | {statusLabel ? ( |
| 89 | 86 | <Text |
| 90 | 87 | className={cn( |
| 91 | − "text-[12px] leading-[16px]", | |
| 88 | + "text-xs leading-[16px]", | |
| 92 | 89 | hasConnectionFailure ? "text-rose-500 dark:text-rose-400" : "text-foreground-muted", |
| 93 | 90 | )} |
| 94 | 91 | numberOfLines={props.expanded ? undefined : 1} |
| @@ -137,14 +134,14 @@ export function ConnectionEnvironmentRow(props: { · 42 unmodified lines | ||
| 137 | 134 | className="gap-3 px-4 pb-4" |
| 138 | 135 | > |
| 139 | 136 | {props.environment.isRelayManaged ? ( |
| 140 | − <Text className="text-[13px] leading-[18px] text-foreground-muted"> | |
| 137 | + <Text className="text-sm leading-[18px] text-foreground-muted"> | |
| 141 | 138 | Managed by T3 Cloud. Tunnel details update automatically. |
| 142 | 139 | </Text> |
| 143 | 140 | ) : ( |
| 144 | 141 | <> |
| 145 | 142 | <View className="gap-1.5"> |
| 146 | 143 | <Text |
| 147 | − className="text-[11px] font-t3-bold uppercase text-foreground-muted" | |
| 144 | + className="text-2xs font-t3-bold uppercase text-foreground-muted" | |
| 148 | 145 | style={{ letterSpacing: 0.8 }} |
| 149 | 146 | > |
| 150 | 147 | Label |
| @@ -156,13 +153,13 @@ export function ConnectionEnvironmentRow(props: { · 5 unmodified lines | ||
| 156 | 153 | placeholderTextColor={placeholderColor} |
| 157 | 154 | value={label} |
| 158 | 155 | onChangeText={setLabel} |
| 159 | − className="rounded-[14px] border border-input-border bg-input px-4 py-3 text-[15px] text-foreground" | |
| 156 | + className="rounded-[14px] border border-input-border bg-input px-4 py-3 text-base text-foreground" | |
| 160 | 157 | /> |
| 161 | 158 | </View> |
| 162 | 159 | |
| 163 | 160 | <View className="gap-1.5"> |
| 164 | 161 | <Text |
| 165 | − className="text-[11px] font-t3-bold uppercase text-foreground-muted" | |
| 162 | + className="text-2xs font-t3-bold uppercase text-foreground-muted" | |
| 166 | 163 | style={{ letterSpacing: 0.8 }} |
| 167 | 164 | > |
| 168 | 165 | URL |
| @@ -175,7 +172,7 @@ export function ConnectionEnvironmentRow(props: { · 6 unmodified lines | ||
| 175 | 172 | placeholderTextColor={placeholderColor} |
| 176 | 173 | value={url} |
| 177 | 174 | onChangeText={setUrl} |
| 178 | − className="rounded-[14px] border border-input-border bg-input px-4 py-3 text-[15px] text-foreground" | |
| 175 | + className="rounded-[14px] border border-input-border bg-input px-4 py-3 text-base text-foreground" | |
| 179 | 176 | /> |
| 180 | 177 | </View> |
| 181 | 178 | </> |
| @@ -189,7 +186,7 @@ export function ConnectionEnvironmentRow(props: { · 7 unmodified lines | ||
| 189 | 186 | > |
| 190 | 187 | <SymbolView name="checkmark" size={13} tintColor={primaryFg} type="monochrome" /> |
| 191 | 188 | <Text |
| 192 | − className="text-[12px] font-t3-bold uppercase text-primary-foreground" | |
| 189 | + className="text-xs font-t3-bold uppercase text-primary-foreground" | |
| 193 | 190 | style={{ letterSpacing: 0.8 }} |
| 194 | 191 | > |
| 195 | 192 | Save |
| @@ -104,7 +104,7 @@ export function ConnectionSheetButton(props: { · 103 unmodified lines | ||
| 104 | 104 | type="monochrome" |
| 105 | 105 | /> |
| 106 | 106 | <Text |
| 107 | − className="text-[12px] font-t3-bold uppercase" | |
| 107 | + className="text-xs font-t3-bold uppercase" | |
| 108 | 108 | style={{ color: colors.textColor, letterSpacing: 0.8 }} |
| 109 | 109 | > |
| 110 | 110 | {props.label} |
| @@ -73,10 +73,10 @@ export function EnvironmentConnectionNotice(props: { · 72 unmodified lines | ||
| 73 | 73 | /> |
| 74 | 74 | )} |
| 75 | 75 | |
| 76 | − <Text className="text-center text-[17px] font-t3-bold leading-[22px] text-foreground"> | |
| 76 | + <Text className="text-center text-lg font-t3-bold leading-[22px] text-foreground"> | |
| 77 | 77 | {noticeTitle(props.connection.phase, props.environmentLabel)} |
| 78 | 78 | </Text> |
| 79 | − <Text className="text-center text-[14px] leading-[20px] text-foreground-muted"> | |
| 79 | + <Text className="text-center text-sm leading-[20px] text-foreground-muted"> | |
| 80 | 80 | {noticeDetail(props.connection.phase, props.resourceName, props.connection.error)} |
| 81 | 81 | {props.connection.traceId ? ( |
| 82 | 82 | <> |
| @@ -99,7 +99,7 @@ export function EnvironmentConnectionNotice(props: { · 16 unmodified lines | ||
| 99 | 99 | className="mt-1 rounded-full bg-subtle px-4 py-2.5 active:opacity-70" |
| 100 | 100 | onPress={props.onRetry} |
| 101 | 101 | > |
| 102 | − <Text className="text-[13px] font-t3-bold text-foreground">Retry now</Text> | |
| 102 | + <Text className="text-sm font-t3-bold text-foreground">Retry now</Text> | |
| 103 | 103 | </Pressable> |
| 104 | 104 | ) : null} |
| 105 | 105 | </View> |
| @@ -7,6 +7,7 @@ import { · 6 unmodified lines | ||
| 7 | 7 | } from "react-native-nitro-markdown"; |
| 8 | 8 | import { Linking, ScrollView, Text as NativeText, View } from "react-native"; |
| 9 | 9 | |
| 10 | +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 10 | 11 | import { useThemeColor } from "../../lib/useThemeColor"; |
| 11 | 12 | import { |
| 12 | 13 | hasNativeSelectableMarkdownText, |
| @@ -72,8 +73,7 @@ function useMarkdownPreviewStyles(): MarkdownPreviewStyles { · 59 unmodified lines | ||
| 72 | 73 | text: { |
| 73 | 74 | color: body, |
| 74 | 75 | fontFamily: "DMSans_400Regular", |
| 75 | − fontSize: 15, | |
| 76 | − lineHeight: 22, | |
| 76 | + ...MOBILE_TYPOGRAPHY.body, | |
| 77 | 77 | }, |
| 78 | 78 | heading: { |
| 79 | 79 | color: strong, |
| @@ -123,8 +123,7 @@ function useMarkdownPreviewStyles(): MarkdownPreviewStyles { · 43 unmodified lines | ||
| 123 | 123 | skillTextColor: codeText, |
| 124 | 124 | quoteMarkerColor: blockquoteBorder, |
| 125 | 125 | dividerColor: horizontalRule, |
| 126 | − fontSize: 15, | |
| 127 | − lineHeight: 22, | |
| 126 | + ...MOBILE_TYPOGRAPHY.body, | |
| 128 | 127 | fontFamily: "DMSans_400Regular", |
| 129 | 128 | headingFontFamily: "DMSans_700Bold", |
| 130 | 129 | boldFontFamily: "DMSans_700Bold", |
| @@ -64,7 +64,7 @@ const FileTreeRow = memo(function FileTreeRow(props: { · 63 unmodified lines | ||
| 64 | 64 | <PierreEntryIcon path={node.path} kind={node.kind} size={17} /> |
| 65 | 65 | <Text |
| 66 | 66 | className={cn( |
| 67 | − "min-w-0 flex-1 text-[14px] leading-[19px]", | |
| 67 | + "min-w-0 flex-1 text-sm leading-[19px]", | |
| 68 | 68 | selected ? "font-t3-bold text-foreground" : "font-t3-medium text-foreground-secondary", |
| 69 | 69 | )} |
| 70 | 70 | numberOfLines={1} |
| @@ -72,7 +72,7 @@ const FileTreeRow = memo(function FileTreeRow(props: { · 1 unmodified line | ||
| 72 | 72 | {node.name} |
| 73 | 73 | </Text> |
| 74 | 74 | {node.kind === "directory" ? ( |
| 75 | − <Text className="text-[11px] font-t3-medium text-foreground-tertiary"> | |
| 75 | + <Text className="text-2xs font-t3-medium text-foreground-tertiary"> | |
| 76 | 76 | {node.children.length} |
| 77 | 77 | </Text> |
| 78 | 78 | ) : null} |
| @@ -142,10 +142,8 @@ export function FileTreeBrowser(props: { · 63 unmodified lines | ||
| 142 | 142 | <View className="flex-1 bg-sheet"> |
| 143 | 143 | {props.error && props.entries.length === 0 ? ( |
| 144 | 144 | <View className="px-4 py-5"> |
| 145 | − <Text className="text-[13px] font-t3-bold text-foreground">Files unavailable</Text> | |
| 146 | − <Text className="mt-1 text-[12px] leading-[18px] text-foreground-muted"> | |
| 147 | − {props.error} | |
| 148 | − </Text> | |
| 145 | + <Text className="text-sm font-t3-bold text-foreground">Files unavailable</Text> | |
| 146 | + <Text className="mt-1 text-xs leading-[18px] text-foreground-muted">{props.error}</Text> | |
| 149 | 147 | </View> |
| 150 | 148 | ) : ( |
| 151 | 149 | <FlatList |
| @@ -174,8 +172,8 @@ export function FileTreeBrowser(props: { · 22 unmodified lines | ||
| 174 | 172 | <ActivityIndicator size="small" /> |
| 175 | 173 | ) : ( |
| 176 | 174 | <> |
| 177 | − <Text className="text-[13px] font-t3-bold text-foreground">No files found</Text> | |
| 178 | − <Text className="mt-1 text-[12px] leading-[18px] text-foreground-muted"> | |
| 175 | + <Text className="text-sm font-t3-bold text-foreground">No files found</Text> | |
| 176 | + <Text className="mt-1 text-xs leading-[18px] text-foreground-muted"> | |
| 179 | 177 | {props.searchQuery.trim().length > 0 |
| 180 | 178 | ? "Try a different search." |
| 181 | 179 | : "The workspace file index is empty."} |
| @@ -18,6 +18,7 @@ import { · 17 unmodified lines | ||
| 18 | 18 | } from "../review/reviewDiffRendering"; |
| 19 | 19 | import type { ReviewHighlightedToken } from "../review/shikiReviewHighlighter"; |
| 20 | 20 | import { cn } from "../../lib/cn"; |
| 21 | +import { MOBILE_CODE_SURFACE } from "../../lib/typography"; | |
| 21 | 22 | import { |
| 22 | 23 | buildNativeSourceRows, |
| 23 | 24 | buildNativeSourceTokens, |
| @@ -28,8 +29,8 @@ import { · 4 unmodified lines | ||
| 28 | 29 | } from "./nativeSourceFileAdapter"; |
| 29 | 30 | import { sourceHighlightAtom } from "./sourceHighlightingState"; |
| 30 | 31 | |
| 31 | −const SOURCE_LINE_HEIGHT = 24; | |
| 32 | −const SOURCE_LINE_NUMBER_WIDTH = 58; | |
| 32 | +const SOURCE_LINE_HEIGHT = MOBILE_CODE_SURFACE.rowHeight; | |
| 33 | +const SOURCE_LINE_NUMBER_WIDTH = MOBILE_CODE_SURFACE.gutterWidth; | |
| 33 | 34 | const NATIVE_SOURCE_STYLE_JSON = JSON.stringify(NATIVE_SOURCE_STYLE); |
| 34 | 35 | |
| 35 | 36 | interface SourceFileSurfaceProps { |
| @@ -56,10 +57,12 @@ const HighlightedSourceLine = memo(function HighlightedSourceLine(props: { · 20 unmodified lines | ||
| 56 | 57 | style={{ minHeight: SOURCE_LINE_HEIGHT }} |
| 57 | 58 | > |
| 58 | 59 | <NativeText |
| 59 | − className="select-none pr-3 text-right text-[11px] leading-[24px] text-foreground-tertiary" | |
| 60 | + className="select-none pr-3 text-right text-foreground-tertiary" | |
| 60 | 61 | style={{ |
| 61 | 62 | width: SOURCE_LINE_NUMBER_WIDTH, |
| 62 | 63 | fontFamily: REVIEW_MONO_FONT_FAMILY, |
| 64 | + fontSize: MOBILE_CODE_SURFACE.lineNumberFontSize, | |
| 65 | + lineHeight: MOBILE_CODE_SURFACE.rowHeight, | |
| 63 | 66 | }} |
| 64 | 67 | > |
| 65 | 68 | {props.index + 1} |
| @@ -67,8 +70,13 @@ const HighlightedSourceLine = memo(function HighlightedSourceLine(props: { · 1 unmodified line | ||
| 67 | 70 | <NativeText |
| 68 | 71 | selectable |
| 69 | 72 | numberOfLines={1} |
| 70 | − className="text-[13px] font-medium leading-[24px] text-foreground" | |
| 71 | − style={{ fontFamily: REVIEW_MONO_FONT_FAMILY, minWidth: 320 }} | |
| 73 | + className="font-normal text-foreground" | |
| 74 | + style={{ | |
| 75 | + fontFamily: REVIEW_MONO_FONT_FAMILY, | |
| 76 | + fontSize: MOBILE_CODE_SURFACE.fontSize, | |
| 77 | + lineHeight: MOBILE_CODE_SURFACE.rowHeight, | |
| 78 | + minWidth: 320, | |
| 79 | + }} | |
| 72 | 80 | > |
| 73 | 81 | {props.tokens && props.tokens.length > 0 |
| 74 | 82 | ? (() => { |
| @@ -80,7 +88,7 @@ const HighlightedSourceLine = memo(function HighlightedSourceLine(props: { · 5 unmodified lines | ||
| 80 | 88 | const fontWeight = |
| 81 | 89 | token.fontStyle !== null && (token.fontStyle & 2) === 2 |
| 82 | 90 | ? ("700" as const) |
| 83 | − : ("500" as const); | |
| 91 | + : ("400" as const); | |
| 84 | 92 | const fontStyle = |
| 85 | 93 | token.fontStyle !== null && (token.fontStyle & 1) === 1 |
| 86 | 94 | ? ("italic" as const) |
| @@ -142,9 +150,7 @@ function SourceHighlightStatusView(props: { readonly status: SourceHighlightStat · 55 unmodified lines | ||
| 142 | 150 | if (props.status === "error") { |
| 143 | 151 | return ( |
| 144 | 152 | <View className="border-b border-border bg-card px-4 py-2"> |
| 145 | − <Text className="text-[11px] font-t3-medium uppercase text-foreground-muted"> | |
| 146 | − Plain text | |
| 147 | − </Text> | |
| 153 | + <Text className="text-2xs font-t3-medium uppercase text-foreground-muted">Plain text</Text> | |
| 148 | 154 | </View> |
| 149 | 155 | ); |
| 150 | 156 | } |
| @@ -24,6 +24,7 @@ import { EmptyState } from "../../components/EmptyState"; · 23 unmodified lines | ||
| 24 | 24 | import { LoadingScreen } from "../../components/LoadingScreen"; |
| 25 | 25 | import { cn } from "../../lib/cn"; |
| 26 | 26 | import { buildThreadFilesNavigation } from "../../lib/routes"; |
| 27 | +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 27 | 28 | import { useThemeColor } from "../../lib/useThemeColor"; |
| 28 | 29 | import { useThreadSelection } from "../../state/use-thread-selection"; |
| 29 | 30 | import { useSelectedThreadWorktree } from "../../state/use-selected-thread-worktree"; |
| @@ -100,7 +101,7 @@ function ModeButton(props: { · 70 unmodified lines | ||
| 100 | 101 | <SymbolView name={props.icon} size={13} tintColor={iconColor} type="monochrome" /> |
| 101 | 102 | <Text |
| 102 | 103 | className={cn( |
| 103 | − "text-[12px] font-t3-bold", | |
| 104 | + "text-xs font-t3-bold", | |
| 104 | 105 | props.active ? "text-primary-foreground" : "text-foreground-muted", |
| 105 | 106 | )} |
| 106 | 107 | > |
| @@ -187,7 +188,7 @@ function FileBreadcrumbs(props: { readonly projectName: string; readonly relativ · 80 unmodified lines | ||
| 187 | 188 | ) : null} |
| 188 | 189 | <Text |
| 189 | 190 | className={cn( |
| 190 | − "max-w-[180px] px-1 text-[12px]", | |
| 191 | + "max-w-[180px] px-1 text-xs", | |
| 191 | 192 | crumb.kind === "file" |
| 192 | 193 | ? "font-t3-bold text-foreground" |
| 193 | 194 | : "font-t3-medium text-foreground-muted", |
| @@ -308,7 +309,7 @@ function FileContent(props: { · 114 unmodified lines | ||
| 308 | 309 | return ( |
| 309 | 310 | <View className="flex-1 items-center justify-center gap-3 bg-card px-6"> |
| 310 | 311 | <ActivityIndicator /> |
| 311 | − <Text className="text-center text-[13px] text-foreground-muted">Loading file...</Text> | |
| 312 | + <Text className="text-center text-sm text-foreground-muted">Loading file...</Text> | |
| 312 | 313 | </View> |
| 313 | 314 | ); |
| 314 | 315 | } |
| @@ -317,10 +318,10 @@ function FileContent(props: { · 2 unmodified lines | ||
| 317 | 318 | <View className="flex-1 bg-card"> |
| 318 | 319 | {props.truncated ? ( |
| 319 | 320 | <View className="border-b border-amber-200 bg-amber-50 px-4 py-2 dark:border-amber-900/60 dark:bg-amber-950/40"> |
| 320 | − <Text className="text-[11px] font-t3-bold uppercase text-amber-700 dark:text-amber-300"> | |
| 321 | + <Text className="text-2xs font-t3-bold uppercase text-amber-700 dark:text-amber-300"> | |
| 321 | 322 | Partial file |
| 322 | 323 | </Text> |
| 323 | − <Text className="text-[12px] leading-[17px] text-amber-800 dark:text-amber-200"> | |
| 324 | + <Text className="text-xs leading-[17px] text-amber-800 dark:text-amber-200"> | |
| 324 | 325 | Preview limited to the first 1 MB of a truncated file. |
| 325 | 326 | </Text> |
| 326 | 327 | </View> |
| @@ -389,7 +390,7 @@ function FilesHeaderTitle(props: { readonly projectName: string }) { · 62 unmodified lines | ||
| 389 | 390 | style={{ |
| 390 | 391 | color: foregroundColor, |
| 391 | 392 | fontFamily: "DMSans_700Bold", |
| 392 | − fontSize: 18, | |
| 393 | + fontSize: MOBILE_TYPOGRAPHY.headline.fontSize, | |
| 393 | 394 | fontWeight: "900", |
| 394 | 395 | letterSpacing: -0.4, |
| 395 | 396 | }} |
| @@ -401,7 +402,7 @@ function FilesHeaderTitle(props: { readonly projectName: string }) { · 5 unmodified lines | ||
| 401 | 402 | style={{ |
| 402 | 403 | color: secondaryForegroundColor, |
| 403 | 404 | fontFamily: "DMSans_500Medium", |
| 404 | − fontSize: 12, | |
| 405 | + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, | |
| 405 | 406 | fontWeight: "500", |
| 406 | 407 | letterSpacing: 0.2, |
| 407 | 408 | }} |
| @@ -81,7 +81,7 @@ function CachedWorkspaceFileImagePreview(props: { · 80 unmodified lines | ||
| 81 | 81 | return ( |
| 82 | 82 | <View className="flex-1 items-center justify-center gap-3 bg-card px-6"> |
| 83 | 83 | <ActivityIndicator /> |
| 84 | − <Text className="text-center text-[13px] text-foreground-muted">Loading image...</Text> | |
| 84 | + <Text className="text-center text-sm text-foreground-muted">Loading image...</Text> | |
| 85 | 85 | </View> |
| 86 | 86 | ); |
| 87 | 87 | } |
| @@ -102,7 +102,7 @@ export function WorkspaceFileImagePreview(props: { · 14 unmodified lines | ||
| 102 | 102 | return ( |
| 103 | 103 | <View className="flex-1 items-center justify-center gap-3 bg-card px-6"> |
| 104 | 104 | <ActivityIndicator /> |
| 105 | − <Text className="text-center text-[13px] text-foreground-muted"> | |
| 105 | + <Text className="text-center text-sm text-foreground-muted"> | |
| 106 | 106 | Preparing image preview... |
| 107 | 107 | </Text> |
| 108 | 108 | </View> |
| @@ -13,7 +13,7 @@ export function WorkspaceFileWebPreview(props: { readonly uri: string | null }) · 12 unmodified lines | ||
| 13 | 13 | return ( |
| 14 | 14 | <View className="flex-1 items-center justify-center gap-3 bg-card px-6"> |
| 15 | 15 | <ActivityIndicator /> |
| 16 | − <Text className="text-center text-[13px] text-foreground-muted">Preparing preview...</Text> | |
| 16 | + <Text className="text-center text-sm text-foreground-muted">Preparing preview...</Text> | |
| 17 | 17 | </View> |
| 18 | 18 | ); |
| 19 | 19 | } |
| @@ -23,10 +23,8 @@ export function WorkspaceFileWebPreview(props: { readonly uri: string | null }) · 3 unmodified lines | ||
| 23 | 23 | {loadProgress > 0 && loadProgress < 1 ? <LoadingStrip progress={loadProgress} /> : null} |
| 24 | 24 | {loadError ? ( |
| 25 | 25 | <View className="border-b border-border bg-card px-4 py-2"> |
| 26 | − <Text className="text-[12px] font-t3-bold text-foreground">Preview failed</Text> | |
| 27 | − <Text className="mt-0.5 text-[12px] leading-[17px] text-foreground-muted"> | |
| 28 | − {loadError} | |
| 29 | − </Text> | |
| 26 | + <Text className="text-xs font-t3-bold text-foreground">Preview failed</Text> | |
| 27 | + <Text className="mt-0.5 text-xs leading-[17px] text-foreground-muted">{loadError}</Text> | |
| 30 | 28 | </View> |
| 31 | 29 | ) : null} |
| 32 | 30 | <WebView |
| @@ -3,10 +3,30 @@ import { describe, expect, it } from "vite-plus/test"; · 2 unmodified lines | ||
| 3 | 3 | import { |
| 4 | 4 | buildNativeSourceRows, |
| 5 | 5 | buildNativeSourceTokens, |
| 6 | + NATIVE_SOURCE_ROW_HEIGHT, | |
| 7 | + NATIVE_SOURCE_STYLE, | |
| 6 | 8 | nativeSourceRowId, |
| 7 | 9 | } from "./nativeSourceFileAdapter"; |
| 10 | +import { | |
| 11 | + NATIVE_REVIEW_DIFF_ROW_HEIGHT, | |
| 12 | + NATIVE_REVIEW_DIFF_STYLE, | |
| 13 | +} from "../review/nativeReviewDiffAdapter"; | |
| 8 | 14 | |
| 9 | 15 | describe("nativeSourceFileAdapter", () => { |
| 16 | + it("uses the same compact code typography as the diff viewer", () => { | |
| 17 | + expect(NATIVE_SOURCE_ROW_HEIGHT).toBe(NATIVE_REVIEW_DIFF_ROW_HEIGHT); | |
| 18 | + expect(NATIVE_SOURCE_STYLE).toMatchObject({ | |
| 19 | + rowHeight: NATIVE_REVIEW_DIFF_STYLE.rowHeight, | |
| 20 | + gutterWidth: NATIVE_REVIEW_DIFF_STYLE.gutterWidth, | |
| 21 | + codePadding: NATIVE_REVIEW_DIFF_STYLE.codePadding, | |
| 22 | + textVerticalInset: NATIVE_REVIEW_DIFF_STYLE.textVerticalInset, | |
| 23 | + codeFontSize: NATIVE_REVIEW_DIFF_STYLE.codeFontSize, | |
| 24 | + codeFontWeight: NATIVE_REVIEW_DIFF_STYLE.codeFontWeight, | |
| 25 | + lineNumberFontSize: NATIVE_REVIEW_DIFF_STYLE.lineNumberFontSize, | |
| 26 | + lineNumberFontWeight: NATIVE_REVIEW_DIFF_STYLE.lineNumberFontWeight, | |
| 27 | + }); | |
| 28 | + }); | |
| 29 | + | |
| 10 | 30 | it("maps plain source lines onto context rows with stable line numbers", () => { |
| 11 | 31 | expect(buildNativeSourceRows(["const value = 1;", "\treturn value;"])).toEqual([ |
| 12 | 32 | { |
| @@ -3,22 +3,24 @@ import type { · 2 unmodified lines | ||
| 3 | 3 | NativeReviewDiffStyle, |
| 4 | 4 | NativeReviewDiffToken, |
| 5 | 5 | } from "../diffs/nativeReviewDiffSurface"; |
| 6 | +import { MOBILE_CODE_SURFACE, MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 6 | 7 | import type { SourceHighlightTokens } from "./sourceHighlightingState"; |
| 7 | 8 | |
| 8 | −export const NATIVE_SOURCE_ROW_HEIGHT = 24; | |
| 9 | +export const NATIVE_SOURCE_ROW_HEIGHT = MOBILE_CODE_SURFACE.rowHeight; | |
| 9 | 10 | export const NATIVE_SOURCE_CONTENT_WIDTH = 32_000; |
| 10 | 11 | |
| 11 | 12 | export const NATIVE_SOURCE_STYLE: NativeReviewDiffStyle = { |
| 12 | 13 | rowHeight: NATIVE_SOURCE_ROW_HEIGHT, |
| 13 | 14 | contentWidth: NATIVE_SOURCE_CONTENT_WIDTH, |
| 14 | 15 | changeBarWidth: 0, |
| 15 | − gutterWidth: 58, | |
| 16 | − codePadding: 8, | |
| 17 | − codeFontSize: 13, | |
| 18 | − codeFontWeight: "medium", | |
| 19 | − lineNumberFontSize: 11, | |
| 20 | − lineNumberFontWeight: "medium", | |
| 21 | − emptyStateFontSize: 12, | |
| 16 | + gutterWidth: MOBILE_CODE_SURFACE.gutterWidth, | |
| 17 | + codePadding: MOBILE_CODE_SURFACE.codePadding, | |
| 18 | + textVerticalInset: MOBILE_CODE_SURFACE.textVerticalInset, | |
| 19 | + codeFontSize: MOBILE_CODE_SURFACE.fontSize, | |
| 20 | + codeFontWeight: "regular", | |
| 21 | + lineNumberFontSize: MOBILE_CODE_SURFACE.lineNumberFontSize, | |
| 22 | + lineNumberFontWeight: "regular", | |
| 23 | + emptyStateFontSize: MOBILE_TYPOGRAPHY.label.fontSize, | |
| 22 | 24 | emptyStateFontWeight: "medium", |
| 23 | 25 | }; |
| 24 | 26 | |
| @@ -12,6 +12,7 @@ import { Stack } from "expo-router"; · 11 unmodified lines | ||
| 12 | 12 | import { Text as RNText, View } from "react-native"; |
| 13 | 13 | |
| 14 | 14 | import { useThemeColor } from "../../lib/useThemeColor"; |
| 15 | +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 15 | 16 | import type { HomeProjectSortOrder } from "./homeThreadList"; |
| 16 | 17 | |
| 17 | 18 | export interface HomeHeaderEnvironment { |
| @@ -118,7 +119,7 @@ export function HomeHeader(props: { · 100 unmodified lines | ||
| 118 | 119 | <RNText |
| 119 | 120 | style={{ |
| 120 | 121 | fontFamily: "DMSans_700Bold", |
| 121 | − fontSize: 17, | |
| 122 | + fontSize: MOBILE_TYPOGRAPHY.headline.fontSize, | |
| 122 | 123 | color: iconColor, |
| 123 | 124 | letterSpacing: -0.4, |
| 124 | 125 | }} |
| @@ -136,7 +137,7 @@ export function HomeHeader(props: { · 11 unmodified lines | ||
| 136 | 137 | <RNText |
| 137 | 138 | style={{ |
| 138 | 139 | fontFamily: "DMSans_700Bold", |
| 139 | − fontSize: 10, | |
| 140 | + fontSize: MOBILE_TYPOGRAPHY.micro.fontSize, | |
| 140 | 141 | color: mutedColor, |
| 141 | 142 | letterSpacing: 1.1, |
| 142 | 143 | textTransform: "uppercase", |
| @@ -186,7 +186,7 @@ function ProjectGroupLabel(props: { · 185 unmodified lines | ||
| 186 | 186 | workspaceRoot={props.project.workspaceRoot} |
| 187 | 187 | /> |
| 188 | 188 | <Text |
| 189 | − className="flex-1 text-[12px] font-t3-medium uppercase text-foreground-muted" | |
| 189 | + className="flex-1 text-xs font-t3-medium uppercase text-foreground-muted" | |
| 190 | 190 | style={{ letterSpacing: 0.5 }} |
| 191 | 191 | numberOfLines={1} |
| 192 | 192 | > |
| @@ -196,7 +196,7 @@ function ProjectGroupLabel(props: { · 3 unmodified lines | ||
| 196 | 196 | {hiddenCount > 0 ? ( |
| 197 | 197 | <Pressable onPress={props.onToggleExpand} hitSlop={8}> |
| 198 | 198 | <Text |
| 199 | − className="text-[12px] font-t3-medium text-foreground-muted" | |
| 199 | + className="text-xs font-t3-medium text-foreground-muted" | |
| 200 | 200 | style={{ letterSpacing: 0.4 }} |
| 201 | 201 | > |
| 202 | 202 | {props.isExpanded ? "Show less" : `${hiddenCount} more`} |
| @@ -335,7 +335,7 @@ function ThreadRow(props: { · 132 unmodified lines | ||
| 335 | 335 | <View style={{ flex: 1, gap: 3 }}> |
| 336 | 336 | <View className="flex-row items-center justify-between gap-2"> |
| 337 | 337 | <Text |
| 338 | − className="flex-1 text-[15px] font-t3-bold leading-[20px] text-foreground" | |
| 338 | + className="flex-1 text-base font-t3-bold leading-[20px] text-foreground" | |
| 339 | 339 | numberOfLines={1} |
| 340 | 340 | > |
| 341 | 341 | {props.thread.title} |
| @@ -345,12 +345,12 @@ function ThreadRow(props: { · 3 unmodified lines | ||
| 345 | 345 | className={tone.pillClassName} |
| 346 | 346 | style={{ borderRadius: 99, paddingHorizontal: 6, paddingVertical: 2 }} |
| 347 | 347 | > |
| 348 | − <Text className={`text-[10px] font-t3-bold ${tone.textClassName}`}> | |
| 348 | + <Text className={`text-3xs font-t3-bold ${tone.textClassName}`}> | |
| 349 | 349 | {tone.label} |
| 350 | 350 | </Text> |
| 351 | 351 | </View> |
| 352 | 352 | <Text |
| 353 | − className="text-[12px] text-foreground-tertiary" | |
| 353 | + className="text-xs text-foreground-tertiary" | |
| 354 | 354 | style={{ fontVariant: ["tabular-nums"] }} |
| 355 | 355 | > |
| 356 | 356 | {timestamp} |
| @@ -367,7 +367,7 @@ function ThreadRow(props: { · 10 unmodified lines | ||
| 367 | 367 | type="monochrome" |
| 368 | 368 | /> |
| 369 | 369 | <Text |
| 370 | − className="text-[11px] text-foreground-tertiary" | |
| 370 | + className="text-2xs text-foreground-tertiary" | |
| 371 | 371 | numberOfLines={1} |
| 372 | 372 | style={{ fontFamily: "monospace" }} |
| 373 | 373 | > |
| @@ -429,7 +429,7 @@ function StaleCatalogStatusPill(props: { · 55 unmodified lines | ||
| 429 | 429 | weight="semibold" |
| 430 | 430 | /> |
| 431 | 431 | )} |
| 432 | − <Text className="max-w-[260px] text-[13px] font-t3-bold text-foreground" numberOfLines={1}> | |
| 432 | + <Text className="max-w-[260px] text-sm font-t3-bold text-foreground" numberOfLines={1}> | |
| 433 | 433 | {label} |
| 434 | 434 | </Text> |
| 435 | 435 | </Pressable> |
| @@ -167,7 +167,7 @@ function SwipeActionButton(props: { · 166 unmodified lines | ||
| 167 | 167 | </Animated.View> |
| 168 | 168 | </View> |
| 169 | 169 | <Animated.View style={[{ paddingTop: 2 }, labelStyle]}> |
| 170 | − <Text className="text-[10px] font-t3-medium text-foreground-muted">{props.label}</Text> | |
| 170 | + <Text className="text-3xs font-t3-medium text-foreground-muted">{props.label}</Text> | |
| 171 | 171 | </Animated.View> |
| 172 | 172 | </Pressable> |
| 173 | 173 | </Animated.View> |
| @@ -100,7 +100,7 @@ function sourceFromParam(value: string | string[] | undefined): AddProjectRemote · 99 unmodified lines | ||
| 100 | 100 | function SectionTitle(props: { readonly children: string }) { |
| 101 | 101 | return ( |
| 102 | 102 | <Text |
| 103 | − className="px-1 text-[11px] font-t3-bold uppercase text-foreground-muted" | |
| 103 | + className="px-1 text-2xs font-t3-bold uppercase text-foreground-muted" | |
| 104 | 104 | style={{ letterSpacing: 0.7 }} |
| 105 | 105 | > |
| 106 | 106 | {props.children} |
| @@ -168,9 +168,9 @@ function ListRow(props: { · 61 unmodified lines | ||
| 168 | 168 | {props.icon} |
| 169 | 169 | </View> |
| 170 | 170 | <View className="flex-1 gap-0.5"> |
| 171 | − <Text className="text-[16px] leading-[21px] font-t3-bold">{props.title}</Text> | |
| 171 | + <Text className="text-base leading-[21px] font-t3-bold">{props.title}</Text> | |
| 172 | 172 | {props.subtitle ? ( |
| 173 | − <Text className="text-[13px] leading-[17px] text-foreground-muted" numberOfLines={2}> | |
| 173 | + <Text className="text-sm leading-[17px] text-foreground-muted" numberOfLines={2}> | |
| 174 | 174 | {props.subtitle} |
| 175 | 175 | </Text> |
| 176 | 176 | ) : null} |
| @@ -202,7 +202,7 @@ function PrimaryActionButton(props: { · 25 unmodified lines | ||
| 202 | 202 | {props.loading ? ( |
| 203 | 203 | <ActivityIndicator color={String(primaryForeground)} /> |
| 204 | 204 | ) : ( |
| 205 | − <Text className="text-[14px] font-t3-bold text-primary-foreground">{props.label}</Text> | |
| 205 | + <Text className="text-base font-t3-bold text-primary-foreground">{props.label}</Text> | |
| 206 | 206 | )} |
| 207 | 207 | </Pressable> |
| 208 | 208 | ); |
| @@ -215,7 +215,7 @@ function ProjectPathInput(props: { · 6 unmodified lines | ||
| 215 | 215 | }) { |
| 216 | 216 | return ( |
| 217 | 217 | <TextInput |
| 218 | − className="h-12 min-h-12 rounded-[24px] px-4 py-0 text-[15px] leading-[20px]" | |
| 218 | + className="h-12 min-h-12 rounded-[24px] px-4 py-0 text-base leading-[20px]" | |
| 219 | 219 | value={props.value} |
| 220 | 220 | onChangeText={props.onChangeText} |
| 221 | 221 | autoCapitalize="none" |
| @@ -273,15 +273,15 @@ function EmptyEnvironmentState() { · 51 unmodified lines | ||
| 273 | 273 | |
| 274 | 274 | return ( |
| 275 | 275 | <View className="items-center gap-3 rounded-2xl bg-card px-5 py-8"> |
| 276 | − <Text className="text-center text-[17px] font-t3-bold">No environments connected</Text> | |
| 277 | − <Text className="text-center text-[14px] leading-[20px] text-foreground-muted"> | |
| 276 | + <Text className="text-center text-lg font-t3-bold">No environments connected</Text> | |
| 277 | + <Text className="text-center text-sm leading-[20px] text-foreground-muted"> | |
| 278 | 278 | Add an environment before adding a project. |
| 279 | 279 | </Text> |
| 280 | 280 | <Pressable |
| 281 | 281 | onPress={() => router.replace("/connections/new")} |
| 282 | 282 | className="mt-1 rounded-full bg-primary px-4 py-2.5 active:opacity-70" |
| 283 | 283 | > |
| 284 | − <Text className="text-[13px] font-t3-bold text-primary-foreground">Add environment</Text> | |
| 284 | + <Text className="text-sm font-t3-bold text-primary-foreground">Add environment</Text> | |
| 285 | 285 | </Pressable> |
| 286 | 286 | </View> |
| 287 | 287 | ); |
| @@ -565,7 +565,7 @@ export function AddProjectRepositoryScreen() { · 277 unmodified lines | ||
| 565 | 565 | <AddProjectShell> |
| 566 | 566 | {error ? <ErrorBanner message={error} /> : null} |
| 567 | 567 | <TextInput |
| 568 | − className="h-12 min-h-12 rounded-[24px] px-4 py-0 text-[15px] leading-[20px]" | |
| 568 | + className="h-12 min-h-12 rounded-[24px] px-4 py-0 text-base leading-[20px]" | |
| 569 | 569 | value={repositoryInput} |
| 570 | 570 | onChangeText={setRepositoryInput} |
| 571 | 571 | autoCapitalize="none" |
| @@ -806,8 +806,8 @@ export function AddProjectDestinationScreen() { · 234 unmodified lines | ||
| 806 | 806 | {error ? <ErrorBanner message={error} /> : null} |
| 807 | 807 | {repositoryTitle ? ( |
| 808 | 808 | <View className="rounded-[24px] bg-card px-4 py-3"> |
| 809 | − <Text className="text-[14px] font-t3-bold">{repositoryTitle}</Text> | |
| 810 | − <Text className="mt-0.5 text-[12px] text-foreground-muted" numberOfLines={2}> | |
| 809 | + <Text className="text-base font-t3-bold">{repositoryTitle}</Text> | |
| 810 | + <Text className="mt-0.5 text-xs text-foreground-muted" numberOfLines={2}> | |
| 811 | 811 | {remoteUrl} |
| 812 | 812 | </Text> |
| 813 | 813 | </View> |
| @@ -159,26 +159,26 @@ export function ReviewCommentComposerSheet() { · 158 unmodified lines | ||
| 159 | 159 | <SymbolView name="xmark" size={18} tintColor={iconTint} type="monochrome" /> |
| 160 | 160 | </Pressable> |
| 161 | 161 | |
| 162 | − <Text className="text-[18px] font-t3-bold text-foreground">Add Comment</Text> | |
| 162 | + <Text className="text-lg font-t3-bold text-foreground">Add Comment</Text> | |
| 163 | 163 | |
| 164 | 164 | <View className="h-12 w-12" /> |
| 165 | 165 | </View> |
| 166 | 166 | |
| 167 | 167 | {!target ? ( |
| 168 | 168 | <View className="rounded-[22px] border border-border bg-card px-4 py-5"> |
| 169 | − <Text className="text-[15px] font-t3-bold text-foreground">No selection</Text> | |
| 170 | − <Text className="mt-1 text-[13px] leading-[19px] text-foreground-muted"> | |
| 169 | + <Text className="text-base font-t3-bold text-foreground">No selection</Text> | |
| 170 | + <Text className="mt-1 text-sm leading-[19px] text-foreground-muted"> | |
| 171 | 171 | Select a diff line or range first. |
| 172 | 172 | </Text> |
| 173 | 173 | </View> |
| 174 | 174 | ) : ( |
| 175 | 175 | <View className="min-h-0 flex-1 gap-4"> |
| 176 | 176 | <View className="gap-1 px-1"> |
| 177 | − <Text className="text-[11px] font-t3-bold uppercase text-foreground-muted"> | |
| 177 | + <Text className="text-2xs font-t3-bold uppercase text-foreground-muted"> | |
| 178 | 178 | {selectionLabel} |
| 179 | 179 | </Text> |
| 180 | 180 | <Text |
| 181 | − className="font-mono text-[12px] leading-[17px] text-foreground-muted" | |
| 181 | + className="font-mono text-xs leading-[17px] text-foreground-muted" | |
| 182 | 182 | ellipsizeMode="middle" |
| 183 | 183 | numberOfLines={2} |
| 184 | 184 | > |
| @@ -215,7 +215,7 @@ export function ReviewCommentComposerSheet() { · 30 unmodified lines | ||
| 215 | 215 | > |
| 216 | 216 | <ReviewChangeBar change={line.change} /> |
| 217 | 217 | <Text |
| 218 | − className="w-9 py-1 pr-1 text-right text-[11px] font-t3-medium text-foreground-muted" | |
| 218 | + className="w-9 py-1 pr-1 text-right text-2xs font-t3-medium text-foreground-muted" | |
| 219 | 219 | style={{ fontFamily: REVIEW_MONO_FONT_FAMILY }} |
| 220 | 220 | > |
| 221 | 221 | {lineNumber ?? ""} |
| @@ -236,7 +236,7 @@ export function ReviewCommentComposerSheet() { · 14 unmodified lines | ||
| 236 | 236 | </View> |
| 237 | 237 | |
| 238 | 238 | <View className="min-h-0 flex-1 gap-2"> |
| 239 | − <Text className="text-[13px] font-t3-bold text-foreground">Comment</Text> | |
| 239 | + <Text className="text-sm font-t3-bold text-foreground">Comment</Text> | |
| 240 | 240 | <View className="min-h-[132px] flex-1 overflow-hidden rounded-[20px] border border-border bg-card"> |
| 241 | 241 | <View className="min-h-0 flex-1 px-4 pt-3.5"> |
| 242 | 242 | <TextInputWrapper onPaste={handleNativePaste} style={{ flex: 1, minHeight: 0 }}> |
| @@ -248,7 +248,7 @@ export function ReviewCommentComposerSheet() { · 5 unmodified lines | ||
| 248 | 248 | textAlignVertical="top" |
| 249 | 249 | value={commentText} |
| 250 | 250 | onChangeText={setCommentText} |
| 251 | − className="h-full flex-1 border-0 bg-transparent px-0 py-0 font-sans text-[15px]" | |
| 251 | + className="h-full flex-1 border-0 bg-transparent px-0 py-0 font-sans text-base" | |
| 252 | 252 | style={{ flex: 1, minHeight: 0 }} |
| 253 | 253 | /> |
| 254 | 254 | </TextInputWrapper> |
| @@ -20,6 +20,7 @@ import { environmentCatalog } from "../../connection/catalog"; · 19 unmodified lines | ||
| 20 | 20 | import { useEnvironmentPresentation } from "../../state/presentation"; |
| 21 | 21 | import { useAtomCommand } from "../../state/use-atom-command"; |
| 22 | 22 | import { useThemeColor } from "../../lib/useThemeColor"; |
| 23 | +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 23 | 24 | import { useThreadDraftForThread } from "../../state/use-thread-composer-state"; |
| 24 | 25 | import { EnvironmentConnectionNotice } from "../connection/EnvironmentConnectionNotice"; |
| 25 | 26 | import { useReviewCacheForThread } from "./reviewState"; |
| @@ -41,10 +42,10 @@ const REVIEW_HEADER_SPACING = 0; · 15 unmodified lines | ||
| 41 | 42 | const ReviewNotice = memo(function ReviewNotice(props: { readonly notice: string }) { |
| 42 | 43 | return ( |
| 43 | 44 | <View className="border-b border-amber-200 bg-amber-50 px-4 py-3 dark:border-amber-900/60 dark:bg-amber-950/40"> |
| 44 | − <Text className="text-[12px] font-t3-bold uppercase text-amber-700 dark:text-amber-300"> | |
| 45 | + <Text className="text-xs font-t3-bold uppercase text-amber-700 dark:text-amber-300"> | |
| 45 | 46 | Partial diff |
| 46 | 47 | </Text> |
| 47 | − <Text className="text-[12px] leading-[18px] text-amber-800 dark:text-amber-200"> | |
| 48 | + <Text className="text-xs leading-[18px] text-amber-800 dark:text-amber-200"> | |
| 48 | 49 | {props.notice} |
| 49 | 50 | </Text> |
| 50 | 51 | </View> |
| @@ -69,7 +70,7 @@ function ReviewSelectionActionBar(props: { · 18 unmodified lines | ||
| 69 | 70 | tintColor="#ffffff" |
| 70 | 71 | type="monochrome" |
| 71 | 72 | /> |
| 72 | − <Text className="text-[15px] font-t3-bold text-white">{props.title}</Text> | |
| 73 | + <Text className="text-base font-t3-bold text-white">{props.title}</Text> | |
| 73 | 74 | </> |
| 74 | 75 | ); |
| 75 | 76 | |
| @@ -218,8 +219,8 @@ export function ReviewSheet() { · 142 unmodified lines | ||
| 218 | 219 | if (error) { |
| 219 | 220 | children.push( |
| 220 | 221 | <View key="review-error" className="border-b border-border bg-card px-4 py-3"> |
| 221 | − <Text className="text-[13px] font-t3-bold text-foreground">Review unavailable</Text> | |
| 222 | − <Text className="text-[12px] leading-[18px] text-foreground-muted">{error}</Text> | |
| 222 | + <Text className="text-sm font-t3-bold text-foreground">Review unavailable</Text> | |
| 223 | + <Text className="text-xs leading-[18px] text-foreground-muted">{error}</Text> | |
| 223 | 224 | </View>, |
| 224 | 225 | ); |
| 225 | 226 | } |
| @@ -251,7 +252,7 @@ export function ReviewSheet() { · 25 unmodified lines | ||
| 251 | 252 | numberOfLines={1} |
| 252 | 253 | style={{ |
| 253 | 254 | fontFamily: "DMSans_700Bold", |
| 254 | − fontSize: 18, | |
| 255 | + fontSize: MOBILE_TYPOGRAPHY.headline.fontSize, | |
| 255 | 256 | fontWeight: "900", |
| 256 | 257 | color: headerForeground, |
| 257 | 258 | letterSpacing: -0.4, |
| @@ -273,7 +274,7 @@ export function ReviewSheet() { · 15 unmodified lines | ||
| 273 | 274 | <NativeText |
| 274 | 275 | style={{ |
| 275 | 276 | fontFamily: "DMSans_700Bold", |
| 276 | − fontSize: 12, | |
| 277 | + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, | |
| 277 | 278 | fontWeight: "700", |
| 278 | 279 | color: "#16a34a", |
| 279 | 280 | }} |
| @@ -283,7 +284,7 @@ export function ReviewSheet() { · 3 unmodified lines | ||
| 283 | 284 | <NativeText |
| 284 | 285 | style={{ |
| 285 | 286 | fontFamily: "DMSans_700Bold", |
| 286 | − fontSize: 12, | |
| 287 | + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, | |
| 287 | 288 | fontWeight: "700", |
| 288 | 289 | color: "#e11d48", |
| 289 | 290 | }} |
| @@ -294,7 +295,7 @@ export function ReviewSheet() { · 4 unmodified lines | ||
| 294 | 295 | <NativeText |
| 295 | 296 | style={{ |
| 296 | 297 | fontFamily: "DMSans_700Bold", |
| 297 | − fontSize: 12, | |
| 298 | + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, | |
| 298 | 299 | fontWeight: "700", |
| 299 | 300 | color: "#b45309", |
| 300 | 301 | }} |
| @@ -309,7 +310,7 @@ export function ReviewSheet() { · 8 unmodified lines | ||
| 309 | 310 | numberOfLines={1} |
| 310 | 311 | style={{ |
| 311 | 312 | fontFamily: "DMSans_700Bold", |
| 312 | − fontSize: 12, | |
| 313 | + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, | |
| 313 | 314 | fontWeight: "700", |
| 314 | 315 | color: headerMuted, |
| 315 | 316 | }} |
| @@ -320,7 +321,7 @@ export function ReviewSheet() { · 4 unmodified lines | ||
| 320 | 321 | <NativeText |
| 321 | 322 | style={{ |
| 322 | 323 | fontFamily: "DMSans_700Bold", |
| 323 | − fontSize: 12, | |
| 324 | + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, | |
| 324 | 325 | fontWeight: "700", |
| 325 | 326 | color: "#b45309", |
| 326 | 327 | }} |
| @@ -430,30 +431,30 @@ export function ReviewSheet() { · 103 unmodified lines | ||
| 430 | 431 | {listHeader} |
| 431 | 432 | {!selectedSection ? ( |
| 432 | 433 | <View className="border-b border-border bg-card px-4 py-5"> |
| 433 | − <Text className="text-[14px] font-t3-bold text-foreground">No review diffs</Text> | |
| 434 | − <Text className="text-[12px] leading-[18px] text-foreground-muted"> | |
| 434 | + <Text className="text-sm font-t3-bold text-foreground">No review diffs</Text> | |
| 435 | + <Text className="text-xs leading-[18px] text-foreground-muted"> | |
| 435 | 436 | This thread has no ready turn diffs and the worktree diff is empty. |
| 436 | 437 | </Text> |
| 437 | 438 | </View> |
| 438 | 439 | ) : selectedSection.isLoading && selectedSection.diff === null ? ( |
| 439 | 440 | <View className="items-center gap-3 border-b border-border bg-card px-4 py-6"> |
| 440 | 441 | <ActivityIndicator size="small" /> |
| 441 | − <Text className="text-[12px] text-foreground-muted">Loading diff…</Text> | |
| 442 | + <Text className="text-xs text-foreground-muted">Loading diff…</Text> | |
| 442 | 443 | </View> |
| 443 | 444 | ) : parsedDiff.kind === "empty" ? ( |
| 444 | 445 | <View className="border-b border-border bg-card px-4 py-5"> |
| 445 | − <Text className="text-[14px] font-t3-bold text-foreground">No changes</Text> | |
| 446 | − <Text className="text-[12px] leading-[18px] text-foreground-muted"> | |
| 446 | + <Text className="text-sm font-t3-bold text-foreground">No changes</Text> | |
| 447 | + <Text className="text-xs leading-[18px] text-foreground-muted"> | |
| 447 | 448 | {selectedSection.subtitle ?? "This diff is empty."} |
| 448 | 449 | </Text> |
| 449 | 450 | </View> |
| 450 | 451 | ) : parsedDiff.kind === "raw" ? ( |
| 451 | 452 | <View className="gap-3 border-b border-border bg-card px-4 py-4"> |
| 452 | − <Text className="text-[12px] leading-[18px] text-foreground-muted"> | |
| 453 | + <Text className="text-xs leading-[18px] text-foreground-muted"> | |
| 453 | 454 | {parsedDiff.reason} |
| 454 | 455 | </Text> |
| 455 | 456 | <ScrollView horizontal showsHorizontalScrollIndicator={false} bounces={false}> |
| 456 | − <Text selectable className="font-mono text-[12px] leading-[19px] text-foreground"> | |
| 457 | + <Text selectable className="font-mono text-xs leading-[19px] text-foreground"> | |
| 457 | 458 | {parsedDiff.text} |
| 458 | 459 | </Text> |
| 459 | 460 | </ScrollView> |
| @@ -5,6 +5,7 @@ import type { · 4 unmodified lines | ||
| 5 | 5 | } from "../diffs/nativeReviewDiffTypes"; |
| 6 | 6 | import * as Arr from "effect/Array"; |
| 7 | 7 | import { pipe } from "effect/Function"; |
| 8 | +import { MOBILE_CODE_SURFACE } from "../../lib/typography"; | |
| 8 | 9 | import { getPierreTerminalTheme, type TerminalAppearanceScheme } from "../terminal/terminalTheme"; |
| 9 | 10 | import { computeWordAltDiffRanges } from "./reviewWordDiffs"; |
| 10 | 11 | import { |
| @@ -18,16 +19,16 @@ import type { ReviewInlineComment } from "./reviewCommentSelection"; · 7 unmodified lines | ||
| 18 | 19 | const NATIVE_REVIEW_MAX_WORD_DIFF_RANGE_COUNT = 4; |
| 19 | 20 | const NATIVE_REVIEW_MAX_WORD_DIFF_COVERAGE = 0.45; |
| 20 | 21 | |
| 21 | −export const NATIVE_REVIEW_DIFF_ROW_HEIGHT = 20; | |
| 22 | +export const NATIVE_REVIEW_DIFF_ROW_HEIGHT = MOBILE_CODE_SURFACE.rowHeight; | |
| 22 | 23 | export const NATIVE_REVIEW_DIFF_CONTENT_WIDTH = 2_800; |
| 23 | 24 | |
| 24 | 25 | export const NATIVE_REVIEW_DIFF_STYLE = { |
| 25 | 26 | rowHeight: NATIVE_REVIEW_DIFF_ROW_HEIGHT, |
| 26 | 27 | contentWidth: NATIVE_REVIEW_DIFF_CONTENT_WIDTH, |
| 27 | 28 | changeBarWidth: 4, |
| 28 | − gutterWidth: 46, | |
| 29 | − codePadding: 7, | |
| 30 | − textVerticalInset: 2, | |
| 29 | + gutterWidth: MOBILE_CODE_SURFACE.gutterWidth, | |
| 30 | + codePadding: MOBILE_CODE_SURFACE.codePadding, | |
| 31 | + textVerticalInset: MOBILE_CODE_SURFACE.textVerticalInset, | |
| 31 | 32 | fileHeaderHeight: 56, |
| 32 | 33 | fileHeaderHorizontalMargin: 8, |
| 33 | 34 | fileHeaderVerticalMargin: 6, |
| @@ -36,9 +37,9 @@ export const NATIVE_REVIEW_DIFF_STYLE = { · 2 unmodified lines | ||
| 36 | 37 | fileHeaderPathRightPadding: 118, |
| 37 | 38 | fileHeaderCountColumnWidth: 38, |
| 38 | 39 | fileHeaderCountGap: 5, |
| 39 | − codeFontSize: 11, | |
| 40 | + codeFontSize: MOBILE_CODE_SURFACE.fontSize, | |
| 40 | 41 | codeFontWeight: "regular", |
| 41 | − lineNumberFontSize: 10, | |
| 42 | + lineNumberFontSize: MOBILE_CODE_SURFACE.lineNumberFontSize, | |
| 42 | 43 | lineNumberFontWeight: "regular", |
| 43 | 44 | hunkFontSize: 11, |
| 44 | 45 | hunkFontWeight: "medium", |
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | import { Platform, Text as NativeText, View } from "react-native"; |
| 2 | 2 | |
| 3 | 3 | import { cn } from "../../lib/cn"; |
| 4 | +import { MOBILE_CODE_SURFACE } from "../../lib/typography"; | |
| 4 | 5 | |
| 5 | 6 | import type { ReviewRenderableLineRow } from "./reviewModel"; |
| 6 | 7 | import type { ReviewHighlightedToken } from "./shikiReviewHighlighter"; |
| @@ -11,7 +12,7 @@ export const REVIEW_MONO_FONT_FAMILY = Platform.select({ · 4 unmodified lines | ||
| 11 | 12 | default: "monospace", |
| 12 | 13 | }); |
| 13 | 14 | |
| 14 | −export const REVIEW_DIFF_LINE_HEIGHT = 26; | |
| 15 | +export const REVIEW_DIFF_LINE_HEIGHT = MOBILE_CODE_SURFACE.rowHeight; | |
| 15 | 16 | const REVIEW_DELETE_STRIPE_COUNT = REVIEW_DIFF_LINE_HEIGHT / 2; |
| 16 | 17 | |
| 17 | 18 | export function renderVisibleWhitespace(value: string): string { |
| @@ -71,8 +72,12 @@ export function DiffTokenText(props: { · 53 unmodified lines | ||
| 71 | 72 | <NativeText |
| 72 | 73 | numberOfLines={1} |
| 73 | 74 | selectable |
| 74 | − className={cn("text-[13px] leading-[17px] font-medium text-foreground", props.className)} | |
| 75 | − style={{ fontFamily: REVIEW_MONO_FONT_FAMILY }} | |
| 75 | + className={cn("font-normal text-foreground", props.className)} | |
| 76 | + style={{ | |
| 77 | + fontFamily: REVIEW_MONO_FONT_FAMILY, | |
| 78 | + fontSize: MOBILE_CODE_SURFACE.fontSize, | |
| 79 | + lineHeight: MOBILE_CODE_SURFACE.rowHeight, | |
| 80 | + }} | |
| 76 | 81 | > |
| 77 | 82 | {renderVisibleWhitespace(props.fallback || " ")} |
| 78 | 83 | </NativeText> |
| @@ -83,8 +88,12 @@ export function DiffTokenText(props: { · 4 unmodified lines | ||
| 83 | 88 | <NativeText |
| 84 | 89 | numberOfLines={1} |
| 85 | 90 | selectable |
| 86 | − className={cn("text-[13px] leading-[17px] font-medium text-foreground", props.className)} | |
| 87 | − style={{ fontFamily: REVIEW_MONO_FONT_FAMILY }} | |
| 91 | + className={cn("font-normal text-foreground", props.className)} | |
| 92 | + style={{ | |
| 93 | + fontFamily: REVIEW_MONO_FONT_FAMILY, | |
| 94 | + fontSize: MOBILE_CODE_SURFACE.fontSize, | |
| 95 | + lineHeight: MOBILE_CODE_SURFACE.rowHeight, | |
| 96 | + }} | |
| 88 | 97 | > |
| 89 | 98 | {(() => { |
| 90 | 99 | let offset = 0; |
| @@ -11,6 +11,7 @@ import { · 10 unmodified lines | ||
| 11 | 11 | } from "react-native"; |
| 12 | 12 | |
| 13 | 13 | import { AppText as Text } from "../../components/AppText"; |
| 14 | +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 14 | 15 | import { resolveNativeTerminalSurfaceView } from "./nativeTerminalModule"; |
| 15 | 16 | import { |
| 16 | 17 | buildGhosttyThemeConfig, |
| @@ -53,7 +54,7 @@ function estimateGridSize(input: { · 36 unmodified lines | ||
| 53 | 54 | } |
| 54 | 55 | |
| 55 | 56 | const FallbackTerminalSurface = memo(function FallbackTerminalSurface(props: TerminalSurfaceProps) { |
| 56 | − const fontSize = props.fontSize ?? 12; | |
| 57 | + const fontSize = props.fontSize ?? MOBILE_TYPOGRAPHY.label.fontSize; | |
| 57 | 58 | const inputRef = useRef<TextInput>(null); |
| 58 | 59 | const appearanceScheme = useColorScheme() === "light" ? "light" : "dark"; |
| 59 | 60 | const theme = props.theme ?? getPierreTerminalTheme(appearanceScheme); |
| @@ -93,7 +94,7 @@ const FallbackTerminalSurface = memo(function FallbackTerminalSurface(props: Ter · 33 unmodified lines | ||
| 93 | 94 | <Text |
| 94 | 95 | style={{ |
| 95 | 96 | color: theme.mutedForeground, |
| 96 | − fontSize: 11, | |
| 97 | + fontSize: MOBILE_TYPOGRAPHY.caption.fontSize, | |
| 97 | 98 | paddingBottom: 8, |
| 98 | 99 | }} |
| 99 | 100 | > |
| @@ -140,7 +141,7 @@ const FallbackTerminalSurface = memo(function FallbackTerminalSurface(props: Ter · 40 unmodified lines | ||
| 140 | 141 | color: theme.foreground, |
| 141 | 142 | flex: 1, |
| 142 | 143 | fontFamily: "Menlo", |
| 143 | − fontSize: 13, | |
| 144 | + fontSize: MOBILE_TYPOGRAPHY.footnote.fontSize, | |
| 144 | 145 | padding: 0, |
| 145 | 146 | }} |
| 146 | 147 | onSubmitEditing={(event) => { |
| @@ -165,7 +166,7 @@ const FallbackTerminalSurface = memo(function FallbackTerminalSurface(props: Ter · 18 unmodified lines | ||
| 165 | 166 | style={{ |
| 166 | 167 | color: theme.foreground, |
| 167 | 168 | fontFamily: "DMSans_700Bold", |
| 168 | − fontSize: 11, | |
| 169 | + fontSize: MOBILE_TYPOGRAPHY.caption.fontSize, | |
| 169 | 170 | }} |
| 170 | 171 | > |
| 171 | 172 | Ctrl-C |
| @@ -177,7 +178,7 @@ const FallbackTerminalSurface = memo(function FallbackTerminalSurface(props: Ter · 5 unmodified lines | ||
| 177 | 178 | }); |
| 178 | 179 | |
| 179 | 180 | export const TerminalSurface = memo(function TerminalSurface(props: TerminalSurfaceProps) { |
| 180 | − const fontSize = props.fontSize ?? 12; | |
| 181 | + const fontSize = props.fontSize ?? MOBILE_TYPOGRAPHY.label.fontSize; | |
| 181 | 182 | const keyboardInputRef = useRef<TextInput>(null); |
| 182 | 183 | const appearanceScheme = useColorScheme() === "light" ? "light" : "dark"; |
| 183 | 184 | const theme = props.theme ?? getPierreTerminalTheme(appearanceScheme); |
| @@ -127,16 +127,16 @@ export const ThreadTerminalPanel = memo(function ThreadTerminalPanel( · 126 unmodified lines | ||
| 127 | 127 | <View className="absolute inset-x-3 bottom-28 top-28 overflow-hidden rounded-[8px] border border-white/10 bg-neutral-950 shadow-2xl"> |
| 128 | 128 | <View className="flex-row items-center justify-between border-b border-white/10 px-3 py-2"> |
| 129 | 129 | <View className="min-w-0 flex-1"> |
| 130 | − <Text className="font-t3-bold text-[13px] text-neutral-100" numberOfLines={1}> | |
| 130 | + <Text className="font-t3-bold text-sm text-neutral-100" numberOfLines={1}> | |
| 131 | 131 | Terminal |
| 132 | 132 | </Text> |
| 133 | − <Text className="text-[11px] text-neutral-500" numberOfLines={1}> | |
| 133 | + <Text className="text-2xs text-neutral-500" numberOfLines={1}> | |
| 134 | 134 | {nativeTerminalAvailable ? "Native Ghostty surface" : "Text fallback active"} |
| 135 | 135 | </Text> |
| 136 | 136 | </View> |
| 137 | 137 | <View className="flex-row items-center gap-2"> |
| 138 | 138 | {terminal.error ? ( |
| 139 | − <Text className="max-w-44 text-right text-[11px] text-red-300" numberOfLines={1}> | |
| 139 | + <Text className="max-w-44 text-right text-2xs text-red-300" numberOfLines={1}> | |
| 140 | 140 | {terminal.error} |
| 141 | 141 | </Text> |
| 142 | 142 | ) : null} |
| @@ -25,6 +25,7 @@ import { terminalEnvironment } from "../../state/terminal"; · 24 unmodified lines | ||
| 25 | 25 | import { useAtomCommand } from "../../state/use-atom-command"; |
| 26 | 26 | import { useWorkspaceState } from "../../state/workspace"; |
| 27 | 27 | import { buildThreadTerminalNavigation } from "../../lib/routes"; |
| 28 | +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 28 | 29 | import { |
| 29 | 30 | useAttachedTerminalSession, |
| 30 | 31 | useKnownTerminalSessions, |
| @@ -920,7 +921,7 @@ export function ThreadTerminalRouteScreen() { · 889 unmodified lines | ||
| 920 | 921 | style={{ |
| 921 | 922 | color: terminalTheme.foreground, |
| 922 | 923 | fontFamily: "DMSans_700Bold", |
| 923 | − fontSize: 13, | |
| 924 | + fontSize: MOBILE_TYPOGRAPHY.footnote.fontSize, | |
| 924 | 925 | lineHeight: 16, |
| 925 | 926 | }} |
| 926 | 927 | > |
| @@ -932,7 +933,7 @@ export function ThreadTerminalRouteScreen() { · 5 unmodified lines | ||
| 932 | 933 | style={{ |
| 933 | 934 | color: terminalTheme.mutedForeground, |
| 934 | 935 | fontFamily: "Menlo", |
| 935 | − fontSize: 11, | |
| 936 | + fontSize: MOBILE_TYPOGRAPHY.caption.fontSize, | |
| 936 | 937 | lineHeight: 14, |
| 937 | 938 | }} |
| 938 | 939 | > |
| @@ -7,6 +7,7 @@ import { Pressable, ScrollView, useColorScheme, View, type ViewStyle } from "rea · 6 unmodified lines | ||
| 7 | 7 | |
| 8 | 8 | import { AppText as Text } from "../../components/AppText"; |
| 9 | 9 | import { PierreEntryIcon } from "../../components/PierreEntryIcon"; |
| 10 | +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 10 | 11 | |
| 11 | 12 | export type ComposerCommandItem = |
| 12 | 13 | | { |
| @@ -156,14 +157,22 @@ const CommandRow = memo(function CommandRow(props: { · 143 unmodified lines | ||
| 156 | 157 | <SymbolView name={iconName} size={14} tintColor={iconColor} type="monochrome" /> |
| 157 | 158 | ) : null} |
| 158 | 159 | <Text |
| 159 | − className="text-[14px] font-t3-medium text-foreground" | |
| 160 | + className="text-base font-t3-medium text-foreground" | |
| 160 | 161 | numberOfLines={1} |
| 161 | 162 | style={{ flexShrink: 0 }} |
| 162 | 163 | > |
| 163 | 164 | {props.item.label} |
| 164 | 165 | </Text> |
| 165 | 166 | {props.item.description ? ( |
| 166 | − <Text numberOfLines={1} style={{ flex: 1, minWidth: 0, fontSize: 12, color: "#a1a1aa" }}> | |
| 167 | + <Text | |
| 168 | + numberOfLines={1} | |
| 169 | + style={{ | |
| 170 | + flex: 1, | |
| 171 | + minWidth: 0, | |
| 172 | + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, | |
| 173 | + color: "#a1a1aa", | |
| 174 | + }} | |
| 175 | + > | |
| 167 | 176 | {props.item.description} |
| 168 | 177 | </Text> |
| 169 | 178 | ) : null} |
| @@ -182,7 +191,7 @@ export const ComposerCommandPopover = memo(function ComposerCommandPopover( · 12 unmodified lines | ||
| 182 | 191 | {label ? ( |
| 183 | 192 | <View style={{ paddingHorizontal: 14, paddingTop: 10, paddingBottom: 4 }}> |
| 184 | 193 | <Text |
| 185 | − className="text-[10px] font-t3-bold text-foreground-muted" | |
| 194 | + className="text-3xs font-t3-bold text-foreground-muted" | |
| 186 | 195 | style={{ letterSpacing: 0.8, textTransform: "uppercase" }} |
| 187 | 196 | > |
| 188 | 197 | {label} |
| @@ -206,7 +215,7 @@ export const ComposerCommandPopover = memo(function ComposerCommandPopover( · 17 unmodified lines | ||
| 206 | 215 | </ScrollView> |
| 207 | 216 | ) : ( |
| 208 | 217 | <View style={{ paddingHorizontal: 14, paddingVertical: 10 }}> |
| 209 | − <Text className="text-[12px] text-foreground-tertiary"> | |
| 218 | + <Text className="text-xs text-foreground-tertiary"> | |
| 210 | 219 | {emptyText(props.triggerKind, props.isLoading)} |
| 211 | 220 | </Text> |
| 212 | 221 | </View> |
| @@ -73,12 +73,12 @@ function OverlayContent(props: { readonly progress: GitActionProgress }) { · 72 unmodified lines | ||
| 73 | 73 | |
| 74 | 74 | <View className="flex-1 gap-0.5"> |
| 75 | 75 | {progress.label ? ( |
| 76 | − <Text className="text-[13px] font-t3-bold text-foreground" numberOfLines={1}> | |
| 76 | + <Text className="text-sm font-t3-bold text-foreground" numberOfLines={1}> | |
| 77 | 77 | {progress.label} |
| 78 | 78 | </Text> |
| 79 | 79 | ) : null} |
| 80 | 80 | {progress.description ? ( |
| 81 | − <Text className="text-[11px] text-foreground-muted" numberOfLines={1}> | |
| 81 | + <Text className="text-2xs text-foreground-muted" numberOfLines={1}> | |
| 82 | 82 | {progress.description} |
| 83 | 83 | </Text> |
| 84 | 84 | ) : null} |
| @@ -30,6 +30,7 @@ import { · 29 unmodified lines | ||
| 30 | 30 | resolveProviderOptionDescriptors, |
| 31 | 31 | } from "../../lib/providerOptions"; |
| 32 | 32 | import { buildThreadRoutePath } from "../../lib/routes"; |
| 33 | +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 33 | 34 | import { useProjects } from "../../state/entities"; |
| 34 | 35 | import { branchBadgeLabel, useNewTaskFlow } from "./new-task-flow-provider"; |
| 35 | 36 | import { useCreateProjectThread } from "./use-project-actions"; |
| @@ -430,7 +431,7 @@ export function NewTaskDraftScreen(props: { · 394 unmodified lines | ||
| 430 | 431 | onPasteImages={(uris) => void handleNativePasteImages(uris)} |
| 431 | 432 | placeholder={`Describe a coding task in ${selectedProject.title}`} |
| 432 | 433 | style={{ flex: 1, minHeight: 0 }} |
| 433 | − textStyle={{ fontSize: 18, lineHeight: 28 }} | |
| 434 | + textStyle={MOBILE_TYPOGRAPHY.composer} | |
| 434 | 435 | /> |
| 435 | 436 | </View> |
| 436 | 437 | |
| @@ -16,7 +16,7 @@ export interface PendingApprovalCardProps { · 15 unmodified lines | ||
| 16 | 16 | export function PendingApprovalCard(props: PendingApprovalCardProps) { |
| 17 | 17 | return ( |
| 18 | 18 | <View className="gap-2.5 rounded-[20px] border border-neutral-200 bg-neutral-100/80 p-4 dark:border-white/6 dark:bg-neutral-900/80"> |
| 19 | − <Text className="font-t3-bold text-[11px] uppercase tracking-[1.1px] text-sky-700 dark:text-sky-300"> | |
| 19 | + <Text className="font-t3-bold text-2xs uppercase tracking-[1.1px] text-sky-700 dark:text-sky-300"> | |
| 20 | 20 | Approval needed |
| 21 | 21 | </Text> |
| 22 | 22 | <Text className="font-t3-bold text-lg text-neutral-950 dark:text-neutral-50"> |
| @@ -26,7 +26,7 @@ export interface PendingUserInputCardProps { · 25 unmodified lines | ||
| 26 | 26 | export function PendingUserInputCard(props: PendingUserInputCardProps) { |
| 27 | 27 | return ( |
| 28 | 28 | <View className="gap-2.5 rounded-[20px] border border-neutral-200 bg-neutral-100/80 p-4 dark:border-white/6 dark:bg-neutral-900/80"> |
| 29 | − <Text className="font-t3-bold text-[11px] uppercase tracking-[1.1px] text-sky-700 dark:text-sky-300"> | |
| 29 | + <Text className="font-t3-bold text-2xs uppercase tracking-[1.1px] text-sky-700 dark:text-sky-300"> | |
| 30 | 30 | User input needed |
| 31 | 31 | </Text> |
| 32 | 32 | <Text className="font-t3-bold text-lg text-neutral-950 dark:text-neutral-50"> |
| @@ -39,7 +39,7 @@ export function PendingUserInputCard(props: PendingUserInputCardProps) { · 6 unmodified lines | ||
| 39 | 39 | <Text className="font-t3-bold text-xs uppercase tracking-[1px] text-neutral-500 dark:text-neutral-500"> |
| 40 | 40 | {question.header} |
| 41 | 41 | </Text> |
| 42 | − <Text className="font-sans text-[15px] leading-[21px] text-neutral-950 dark:text-neutral-50"> | |
| 42 | + <Text className="font-sans text-base leading-[21px] text-neutral-950 dark:text-neutral-50"> | |
| 43 | 43 | {question.question} |
| 44 | 44 | </Text> |
| 45 | 45 | <View className="flex-row flex-wrap gap-2.5"> |
| @@ -65,7 +65,7 @@ export function PendingUserInputCard(props: PendingUserInputCardProps) { · 19 unmodified lines | ||
| 65 | 65 | > |
| 66 | 66 | <Text |
| 67 | 67 | className={cn( |
| 68 | − "font-t3-bold text-[13px]", | |
| 68 | + "font-t3-bold text-sm", | |
| 69 | 69 | selected |
| 70 | 70 | ? "text-sky-700 dark:text-sky-300" |
| 71 | 71 | : "text-neutral-600 dark:text-neutral-300", |
| @@ -83,7 +83,7 @@ export function PendingUserInputCard(props: PendingUserInputCardProps) { · 11 unmodified lines | ||
| 83 | 83 | props.onChangeCustomAnswer(props.pendingUserInput.requestId, question.id, value) |
| 84 | 84 | } |
| 85 | 85 | placeholder="Or type a custom answer" |
| 86 | − className="min-h-[54px] rounded-2xl border border-neutral-200 bg-white px-3.5 py-3 font-sans text-[15px] text-neutral-950 dark:border-white/8 dark:bg-neutral-950/70 dark:text-neutral-50" | |
| 86 | + className="min-h-[54px] rounded-2xl border border-neutral-200 bg-white px-3.5 py-3 font-sans text-base text-neutral-950 dark:border-white/8 dark:bg-neutral-950/70 dark:text-neutral-50" | |
| 87 | 87 | /> |
| 88 | 88 | </View> |
| 89 | 89 | ); |
| @@ -43,6 +43,7 @@ import { ControlPill, ControlPillMenu } from "../../components/ControlPill"; · 42 unmodified lines | ||
| 43 | 43 | import { ProviderIcon } from "../../components/ProviderIcon"; |
| 44 | 44 | import type { DraftComposerImageAttachment } from "../../lib/composerImages"; |
| 45 | 45 | import { buildModelOptions, groupByProvider } from "../../lib/modelOptions"; |
| 46 | +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 46 | 47 | import type { RemoteClientConnectionState } from "../../lib/connection"; |
| 47 | 48 | import { |
| 48 | 49 | insertRankedSearchResult, |
| @@ -189,7 +190,7 @@ const ComposerConnectionStatusPill = memo(function ComposerConnectionStatusPill( · 140 unmodified lines | ||
| 189 | 190 | <View className="h-2 w-2 rounded-full bg-red-500" /> |
| 190 | 191 | )} |
| 191 | 192 | <Text |
| 192 | − className="max-w-[260px] text-[13px] font-t3-bold leading-[17px] text-foreground" | |
| 193 | + className="max-w-[260px] text-sm font-t3-bold leading-[17px] text-foreground" | |
| 193 | 194 | numberOfLines={1} |
| 194 | 195 | > |
| 195 | 196 | {props.status.label} |
| @@ -717,8 +718,7 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer · 521 unmodified lines | ||
| 717 | 718 | } |
| 718 | 719 | } |
| 719 | 720 | textStyle={{ |
| 720 | − fontSize: 15, | |
| 721 | − lineHeight: isExpanded ? 22 : 20, | |
| 721 | + ...MOBILE_TYPOGRAPHY.composer, | |
| 722 | 722 | color: foregroundColor, |
| 723 | 723 | fontFamily: "DMSans_400Regular", |
| 724 | 724 | }} |
| @@ -751,7 +751,7 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer · 26 unmodified lines | ||
| 751 | 751 | justifyContent: "center", |
| 752 | 752 | }} |
| 753 | 753 | > |
| 754 | − <Text className="text-foreground-muted text-[11px] font-t3-bold"> | |
| 754 | + <Text className="text-foreground-muted text-2xs font-t3-bold"> | |
| 755 | 755 | +{props.draftAttachments.length - 3} |
| 756 | 756 | </Text> |
| 757 | 757 | </View> |
| @@ -831,8 +831,7 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer · 73 unmodified lines | ||
| 831 | 831 | <Text |
| 832 | 832 | className="text-foreground-muted" |
| 833 | 833 | style={{ |
| 834 | − fontSize: 12, | |
| 835 | − lineHeight: 18, | |
| 834 | + ...MOBILE_TYPOGRAPHY.label, | |
| 836 | 835 | paddingTop: 8, |
| 837 | 836 | }} |
| 838 | 837 | > |
| @@ -56,6 +56,7 @@ import { buildReviewParsedDiff } from "../review/reviewModel"; · 55 unmodified lines | ||
| 56 | 56 | import { cn } from "../../lib/cn"; |
| 57 | 57 | import type { LayoutVariant } from "../../lib/layout"; |
| 58 | 58 | import { buildThreadFilesNavigation } from "../../lib/routes"; |
| 59 | +import { MOBILE_CODE_SURFACE, MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 59 | 60 | import { markdownFileIconSource } from "@t3tools/mobile-markdown-text/file-icons"; |
| 60 | 61 | import { resolveMarkdownLinkPresentation } from "@t3tools/mobile-markdown-text/links"; |
| 61 | 62 | import { |
| @@ -444,8 +445,7 @@ function useMarkdownStyles(onLinkPress: (href: string) => void): MarkdownStyleSe · 382 unmodified lines | ||
| 444 | 445 | marginRight: 5, |
| 445 | 446 | color: inlineTextColor, |
| 446 | 447 | fontFamily: "DMSans_400Regular", |
| 447 | − fontSize: 15, | |
| 448 | − lineHeight: 22, | |
| 448 | + ...MOBILE_TYPOGRAPHY.body, | |
| 449 | 449 | textAlign: ordered ? "right" : "center", |
| 450 | 450 | }} |
| 451 | 451 | > |
| @@ -466,7 +466,7 @@ function useMarkdownStyles(onLinkPress: (href: string) => void): MarkdownStyleSe · 14 unmodified lines | ||
| 466 | 466 | style={{ |
| 467 | 467 | color: inlineCodeTextColor, |
| 468 | 468 | fontFamily: "ui-monospace", |
| 469 | − fontSize: 12, | |
| 469 | + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, | |
| 470 | 470 | lineHeight: 22, |
| 471 | 471 | }} |
| 472 | 472 | > |
| @@ -503,7 +503,7 @@ function useMarkdownStyles(onLinkPress: (href: string) => void): MarkdownStyleSe · 30 unmodified lines | ||
| 503 | 503 | style={{ |
| 504 | 504 | color: markdownBodyColor, |
| 505 | 505 | fontFamily: "ui-monospace", |
| 506 | − fontSize: 12, | |
| 506 | + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, | |
| 507 | 507 | opacity: 0.7, |
| 508 | 508 | textTransform: "uppercase", |
| 509 | 509 | }} |
| @@ -523,7 +523,7 @@ function useMarkdownStyles(onLinkPress: (href: string) => void): MarkdownStyleSe · 13 unmodified lines | ||
| 523 | 523 | style={{ |
| 524 | 524 | color: blockTextColor, |
| 525 | 525 | fontFamily: "ui-monospace", |
| 526 | − fontSize: 12, | |
| 526 | + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, | |
| 527 | 527 | lineHeight: 18, |
| 528 | 528 | }} |
| 529 | 529 | > |
| @@ -603,8 +603,7 @@ function useMarkdownStyles(onLinkPress: (href: string) => void): MarkdownStyleSe · 73 unmodified lines | ||
| 603 | 603 | skillTextColor: "#f0abfc", |
| 604 | 604 | quoteMarkerColor: markdownUserBodyColor, |
| 605 | 605 | dividerColor: markdownUserBodyColor, |
| 606 | − fontSize: 15, | |
| 607 | − lineHeight: 22, | |
| 606 | + ...MOBILE_TYPOGRAPHY.body, | |
| 608 | 607 | fontFamily: "DMSans_400Regular", |
| 609 | 608 | headingFontFamily: "DMSans_700Bold", |
| 610 | 609 | boldFontFamily: "DMSans_700Bold", |
| @@ -633,8 +632,7 @@ function useMarkdownStyles(onLinkPress: (href: string) => void): MarkdownStyleSe · 22 unmodified lines | ||
| 633 | 632 | skillTextColor: inlineSkillForeground, |
| 634 | 633 | quoteMarkerColor: markdownBlockquoteBorder, |
| 635 | 634 | dividerColor: markdownHrColor, |
| 636 | − fontSize: 15, | |
| 637 | − lineHeight: 22, | |
| 635 | + ...MOBILE_TYPOGRAPHY.body, | |
| 638 | 636 | fontFamily: "DMSans_400Regular", |
| 639 | 637 | headingFontFamily: "DMSans_700Bold", |
| 640 | 638 | boldFontFamily: "DMSans_700Bold", |
| @@ -821,7 +819,7 @@ function renderFeedEntry( · 180 unmodified lines | ||
| 821 | 819 | className="max-w-[85%] gap-2 rounded-[22px] rounded-br-[6px] px-3.5 py-2.5 opacity-60" |
| 822 | 820 | style={{ backgroundColor: userBubbleColor }} |
| 823 | 821 | > |
| 824 | − <Text className="font-sans text-[15px] leading-[22px] text-white"> | |
| 822 | + <Text className="font-sans text-base leading-[22px] text-white"> | |
| 825 | 823 | {entry.queuedMessage.text} |
| 826 | 824 | </Text> |
| 827 | 825 | {entry.queuedMessage.attachments.length > 0 ? ( |
| @@ -995,7 +993,7 @@ const ReviewCommentCard = memo(function ReviewCommentCard(props: { · 167 unmodified lines | ||
| 995 | 993 | </View> |
| 996 | 994 | <View className="min-w-0 flex-1"> |
| 997 | 995 | <Text |
| 998 | − className="font-mono text-[12px] leading-[16px]" | |
| 996 | + className="font-mono text-xs leading-[16px]" | |
| 999 | 997 | numberOfLines={1} |
| 1000 | 998 | style={{ color: props.colors.text }} |
| 1001 | 999 | > |
| @@ -1040,8 +1038,8 @@ const ReviewCommentCard = memo(function ReviewCommentCard(props: { · 38 unmodified lines | ||
| 1040 | 1038 | style={{ |
| 1041 | 1039 | color: props.colors.text, |
| 1042 | 1040 | fontFamily: "ui-monospace", |
| 1043 | − fontSize: 12, | |
| 1044 | − lineHeight: 18, | |
| 1041 | + fontSize: MOBILE_CODE_SURFACE.fontSize, | |
| 1042 | + lineHeight: MOBILE_CODE_SURFACE.rowHeight, | |
| 1045 | 1043 | }} |
| 1046 | 1044 | > |
| 1047 | 1045 | {props.comment.diff.trim()} |
| @@ -1052,7 +1050,7 @@ const ReviewCommentCard = memo(function ReviewCommentCard(props: { · 4 unmodified lines | ||
| 1052 | 1050 | <View className="border-t px-3 py-3" style={{ borderColor: props.colors.border }}> |
| 1053 | 1051 | <Text |
| 1054 | 1052 | selectable |
| 1055 | − className="text-[15px] leading-[21px]" | |
| 1053 | + className="text-base leading-[21px]" | |
| 1056 | 1054 | style={{ color: props.colors.text }} |
| 1057 | 1055 | > |
| 1058 | 1056 | {props.comment.text} |
| @@ -152,7 +152,7 @@ export function ThreadNavigationDrawer(props: { · 151 unmodified lines | ||
| 152 | 152 | ]} |
| 153 | 153 | > |
| 154 | 154 | <View className="flex-row items-center justify-between px-4 pb-5"> |
| 155 | − <Text className="text-[26px] font-t3-bold">Threads</Text> | |
| 155 | + <Text className="text-2xl font-t3-bold">Threads</Text> | |
| 156 | 156 | <Pressable |
| 157 | 157 | onPress={() => { |
| 158 | 158 | props.onClose(); |
| @@ -224,7 +224,7 @@ function ThreadNavigationDrawerContent(props: { · 65 unmodified lines | ||
| 224 | 224 | {groupedThreads.map((group) => ( |
| 225 | 225 | <View key={group.key} className="gap-3"> |
| 226 | 226 | <Text |
| 227 | − className="px-1 text-[15px] font-t3-bold text-foreground-muted" | |
| 227 | + className="px-1 text-base font-t3-bold text-foreground-muted" | |
| 228 | 228 | style={{ letterSpacing: -0.2 }} |
| 229 | 229 | > |
| 230 | 230 | {group.title} |
| @@ -233,9 +233,7 @@ function ThreadNavigationDrawerContent(props: { · 2 unmodified lines | ||
| 233 | 233 | <View className="overflow-hidden rounded-[22px] bg-card"> |
| 234 | 234 | {group.threads.length === 0 ? ( |
| 235 | 235 | <View className="px-4 py-4"> |
| 236 | − <Text className="text-[14px] font-medium text-foreground-tertiary"> | |
| 237 | − No threads yet | |
| 238 | − </Text> | |
| 236 | + <Text className="text-sm font-medium text-foreground-tertiary">No threads yet</Text> | |
| 239 | 237 | </View> |
| 240 | 238 | ) : ( |
| 241 | 239 | group.threads.map((thread, index) => { |
| @@ -260,11 +258,11 @@ function ThreadNavigationDrawerContent(props: { · 18 unmodified lines | ||
| 260 | 258 | > |
| 261 | 259 | <View className="flex-row items-start justify-between gap-3"> |
| 262 | 260 | <View className="flex-1 gap-1"> |
| 263 | − <Text className="text-[16px] font-t3-bold" numberOfLines={1}> | |
| 261 | + <Text className="text-base font-t3-bold" numberOfLines={1}> | |
| 264 | 262 | {thread.title} |
| 265 | 263 | </Text> |
| 266 | 264 | <Text |
| 267 | − className="text-[13px] font-medium text-foreground-muted" | |
| 265 | + className="text-sm font-medium text-foreground-muted" | |
| 268 | 266 | numberOfLines={1} |
| 269 | 267 | > |
| 270 | 268 | {relativeTime(thread.updatedAt ?? thread.createdAt)} |
| @@ -20,6 +20,7 @@ import { EmptyState } from "../../components/EmptyState"; · 19 unmodified lines | ||
| 20 | 20 | import { LoadingScreen } from "../../components/LoadingScreen"; |
| 21 | 21 | import { buildThreadRoutePath, buildThreadTerminalNavigation } from "../../lib/routes"; |
| 22 | 22 | import { scopedThreadKey } from "../../lib/scopedEntities"; |
| 23 | +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; | |
| 23 | 24 | import { connectionTone } from "../connection/connectionTone"; |
| 24 | 25 | |
| 25 | 26 | import { |
| @@ -390,7 +391,7 @@ export function ThreadRouteScreen() { · 364 unmodified lines | ||
| 390 | 391 | numberOfLines={1} |
| 391 | 392 | style={{ |
| 392 | 393 | fontFamily: "DMSans_700Bold", |
| 393 | − fontSize: 18, | |
| 394 | + fontSize: MOBILE_TYPOGRAPHY.headline.fontSize, | |
| 394 | 395 | fontWeight: "900", |
| 395 | 396 | color: foregroundColor, |
| 396 | 397 | letterSpacing: -0.4, |
| @@ -402,7 +403,7 @@ export function ThreadRouteScreen() { · 5 unmodified lines | ||
| 402 | 403 | numberOfLines={1} |
| 403 | 404 | style={{ |
| 404 | 405 | fontFamily: "DMSans_700Bold", |
| 405 | − fontSize: 12, | |
| 406 | + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, | |
| 406 | 407 | fontWeight: "700", |
| 407 | 408 | color: secondaryFg, |
| 408 | 409 | letterSpacing: 0.3, |
| @@ -69,7 +69,7 @@ export function GitBranchesSheet() { · 68 unmodified lines | ||
| 69 | 69 | > |
| 70 | 70 | <View className="gap-2 rounded-[18px] border border-border bg-card px-4 py-4"> |
| 71 | 71 | <Text |
| 72 | − className="text-foreground-secondary text-[11px] font-t3-bold uppercase" | |
| 72 | + className="text-foreground-secondary text-2xs font-t3-bold uppercase" | |
| 73 | 73 | style={{ letterSpacing: 1 }} |
| 74 | 74 | > |
| 75 | 75 | New branch |
| @@ -78,7 +78,7 @@ export function GitBranchesSheet() { · 2 unmodified lines | ||
| 78 | 78 | value={newBranchName} |
| 79 | 79 | onChangeText={setNewBranchName} |
| 80 | 80 | placeholder="feature/mobile-polish" |
| 81 | − className="rounded-[18px] px-3.5 py-3 font-sans text-[15px]" | |
| 81 | + className="rounded-[18px] px-3.5 py-3 font-sans text-base" | |
| 82 | 82 | style={{ |
| 83 | 83 | borderWidth: 1, |
| 84 | 84 | borderColor: inputBorderColor, |
| @@ -104,7 +104,7 @@ export function GitBranchesSheet() { · 19 unmodified lines | ||
| 104 | 104 | |
| 105 | 105 | <View className="gap-2 rounded-[18px] border border-border bg-card px-4 py-4"> |
| 106 | 106 | <Text |
| 107 | − className="text-foreground-secondary text-[11px] font-t3-bold uppercase" | |
| 107 | + className="text-foreground-secondary text-2xs font-t3-bold uppercase" | |
| 108 | 108 | style={{ letterSpacing: 1 }} |
| 109 | 109 | > |
| 110 | 110 | New worktree |
| @@ -113,7 +113,7 @@ export function GitBranchesSheet() { · 2 unmodified lines | ||
| 113 | 113 | value={worktreeBaseBranch} |
| 114 | 114 | onChangeText={setWorktreeBaseBranch} |
| 115 | 115 | placeholder="main" |
| 116 | − className="rounded-[18px] px-3.5 py-3 font-sans text-[15px]" | |
| 116 | + className="rounded-[18px] px-3.5 py-3 font-sans text-base" | |
| 117 | 117 | style={{ |
| 118 | 118 | borderWidth: 1, |
| 119 | 119 | borderColor: inputBorderColor, |
| @@ -125,7 +125,7 @@ export function GitBranchesSheet() { · 5 unmodified lines | ||
| 125 | 125 | value={worktreeBranchName} |
| 126 | 126 | onChangeText={setWorktreeBranchName} |
| 127 | 127 | placeholder="feature/mobile-thread" |
| 128 | − className="rounded-[18px] px-3.5 py-3 font-sans text-[15px]" | |
| 128 | + className="rounded-[18px] px-3.5 py-3 font-sans text-base" | |
| 129 | 129 | style={{ |
| 130 | 130 | borderWidth: 1, |
| 131 | 131 | borderColor: inputBorderColor, |
| @@ -154,18 +154,16 @@ export function GitBranchesSheet() { · 22 unmodified lines | ||
| 154 | 154 | |
| 155 | 155 | <View className="gap-2"> |
| 156 | 156 | <Text |
| 157 | − className="text-foreground-secondary text-[11px] font-t3-bold uppercase" | |
| 157 | + className="text-foreground-secondary text-2xs font-t3-bold uppercase" | |
| 158 | 158 | style={{ letterSpacing: 1 }} |
| 159 | 159 | > |
| 160 | 160 | Existing branches |
| 161 | 161 | </Text> |
| 162 | 162 | {branchesLoading ? ( |
| 163 | − <Text className="text-foreground-secondary text-[13px] font-medium"> | |
| 164 | − Loading branches... | |
| 165 | − </Text> | |
| 163 | + <Text className="text-foreground-secondary text-sm font-medium">Loading branches...</Text> | |
| 166 | 164 | ) : null} |
| 167 | 165 | {!branchesLoading && availableBranches.length === 0 ? ( |
| 168 | − <Text className="text-foreground-secondary text-[13px] font-medium"> | |
| 166 | + <Text className="text-foreground-secondary text-sm font-medium"> | |
| 169 | 167 | No local branches found. |
| 170 | 168 | </Text> |
| 171 | 169 | ) : null} |
| @@ -195,8 +193,8 @@ export function GitBranchesSheet() { · 23 unmodified lines | ||
| 195 | 193 | }} |
| 196 | 194 | > |
| 197 | 195 | <View className="absolute inset-0 rounded-[18px] bg-card" /> |
| 198 | − <Text className="text-foreground text-[15px] font-t3-bold">{branch.name}</Text> | |
| 199 | − <Text className="text-foreground-secondary text-[12px] font-medium">{subtitle}</Text> | |
| 196 | + <Text className="text-foreground text-base font-t3-bold">{branch.name}</Text> | |
| 197 | + <Text className="text-foreground-secondary text-xs font-medium">{subtitle}</Text> | |
| 200 | 198 | </Pressable> |
| 201 | 199 | ); |
| 202 | 200 | })} |
| @@ -79,14 +79,14 @@ export function GitCommitSheet() { · 78 unmodified lines | ||
| 79 | 79 | > |
| 80 | 80 | <View className="gap-3 rounded-[22px] border border-border bg-card px-4 py-4"> |
| 81 | 81 | <View className="flex-row items-center justify-between gap-3"> |
| 82 | − <Text className="text-foreground-muted text-[13px] font-medium">Branch</Text> | |
| 83 | − <Text className="text-foreground text-[15px] font-t3-bold"> | |
| 82 | + <Text className="text-foreground-muted text-sm font-medium">Branch</Text> | |
| 83 | + <Text className="text-foreground text-base font-t3-bold"> | |
| 84 | 84 | {gitStatus.data?.refName ?? "(detached HEAD)"} |
| 85 | 85 | </Text> |
| 86 | 86 | </View> |
| 87 | 87 | {isDefaultRef ? ( |
| 88 | 88 | <Text |
| 89 | − className="text-[12px] leading-[18px]" | |
| 89 | + className="text-xs leading-[18px]" | |
| 90 | 90 | style={{ color: isDarkMode ? "#fbbf24" : "#b45309" }} |
| 91 | 91 | > |
| 92 | 92 | Warning: this is the default branch. |
| @@ -97,8 +97,8 @@ export function GitCommitSheet() { · 4 unmodified lines | ||
| 97 | 97 | <View className="gap-3 rounded-[22px] border border-border bg-card px-4 py-4"> |
| 98 | 98 | <View className="flex-row items-center justify-between gap-3"> |
| 99 | 99 | <View className="gap-1"> |
| 100 | − <Text className="text-foreground text-[16px] font-t3-bold">Files</Text> | |
| 101 | − <Text className="text-foreground-muted text-[12px] leading-[18px]"> | |
| 100 | + <Text className="text-foreground text-base font-t3-bold">Files</Text> | |
| 101 | + <Text className="text-foreground-muted text-xs leading-[18px]"> | |
| 102 | 102 | {selectedFiles.length} selected · +{selectedInsertions} / -{selectedDeletions} |
| 103 | 103 | </Text> |
| 104 | 104 | </View> |
| @@ -108,14 +108,14 @@ export function GitCommitSheet() { · 3 unmodified lines | ||
| 108 | 108 | className="bg-subtle rounded-full px-3 py-2" |
| 109 | 109 | onPress={() => setExcludedFiles(new Set())} |
| 110 | 110 | > |
| 111 | − <Text className="text-foreground text-[11px] font-t3-bold uppercase">Reset</Text> | |
| 111 | + <Text className="text-foreground text-2xs font-t3-bold uppercase">Reset</Text> | |
| 112 | 112 | </Pressable> |
| 113 | 113 | ) : null} |
| 114 | 114 | <Pressable |
| 115 | 115 | className="bg-subtle rounded-full px-3 py-2" |
| 116 | 116 | onPress={() => setIsEditingFiles((current) => !current)} |
| 117 | 117 | > |
| 118 | − <Text className="text-foreground text-[11px] font-t3-bold uppercase"> | |
| 118 | + <Text className="text-foreground text-2xs font-t3-bold uppercase"> | |
| 119 | 119 | {isEditingFiles ? "Done" : "Edit"} |
| 120 | 120 | </Text> |
| 121 | 121 | </Pressable> |
| @@ -123,26 +123,26 @@ export function GitCommitSheet() { · 1 unmodified line | ||
| 123 | 123 | </View> |
| 124 | 124 | |
| 125 | 125 | {allFiles.length === 0 ? ( |
| 126 | − <Text className="text-foreground-secondary text-[13px] leading-[19px]"> | |
| 126 | + <Text className="text-foreground-secondary text-sm leading-[19px]"> | |
| 127 | 127 | No changed files are available to commit. |
| 128 | 128 | </Text> |
| 129 | 129 | ) : !isEditingFiles ? ( |
| 130 | 130 | <View className="gap-2"> |
| 131 | 131 | {selectedFilePreview.map((file) => ( |
| 132 | 132 | <View key={file.path} className="flex-row items-center justify-between gap-3"> |
| 133 | − <Text className="text-foreground flex-1 text-[13px] font-medium" numberOfLines={1}> | |
| 133 | + <Text className="text-foreground flex-1 text-sm font-medium" numberOfLines={1}> | |
| 134 | 134 | {file.path} |
| 135 | 135 | </Text> |
| 136 | − <Text className="text-[12px] font-t3-bold" style={{ color: "#10b981" }}> | |
| 136 | + <Text className="text-xs font-t3-bold" style={{ color: "#10b981" }}> | |
| 137 | 137 | +{file.insertions} |
| 138 | 138 | </Text> |
| 139 | − <Text className="text-[12px] font-t3-bold" style={{ color: "#f43f5e" }}> | |
| 139 | + <Text className="text-xs font-t3-bold" style={{ color: "#f43f5e" }}> | |
| 140 | 140 | -{file.deletions} |
| 141 | 141 | </Text> |
| 142 | 142 | </View> |
| 143 | 143 | ))} |
| 144 | 144 | {selectedFiles.length > selectedFilePreview.length ? ( |
| 145 | − <Text className="text-foreground-muted text-[12px] leading-[17px]"> | |
| 145 | + <Text className="text-foreground-muted text-xs leading-[17px]"> | |
| 146 | 146 | +{selectedFiles.length - selectedFilePreview.length} more files |
| 147 | 147 | </Text> |
| 148 | 148 | ) : null} |
| @@ -177,21 +177,21 @@ export function GitCommitSheet() { · 28 unmodified lines | ||
| 177 | 177 | <View className="flex-1 gap-1"> |
| 178 | 178 | <Text |
| 179 | 179 | selectable |
| 180 | − className={`text-[13px] font-t3-bold ${included ? "text-foreground" : "text-foreground-muted"}`} | |
| 180 | + className={`text-sm font-t3-bold ${included ? "text-foreground" : "text-foreground-muted"}`} | |
| 181 | 181 | > |
| 182 | 182 | {file.path} |
| 183 | 183 | </Text> |
| 184 | 184 | {!included ? ( |
| 185 | − <Text className="text-foreground-muted text-[11px] leading-[16px]"> | |
| 185 | + <Text className="text-foreground-muted text-2xs leading-[16px]"> | |
| 186 | 186 | Excluded from this commit |
| 187 | 187 | </Text> |
| 188 | 188 | ) : null} |
| 189 | 189 | </View> |
| 190 | 190 | <View className="items-end gap-1"> |
| 191 | − <Text className="text-[12px] font-t3-bold" style={{ color: "#10b981" }}> | |
| 191 | + <Text className="text-xs font-t3-bold" style={{ color: "#10b981" }}> | |
| 192 | 192 | +{file.insertions} |
| 193 | 193 | </Text> |
| 194 | − <Text className="text-[12px] font-t3-bold" style={{ color: "#f43f5e" }}> | |
| 194 | + <Text className="text-xs font-t3-bold" style={{ color: "#f43f5e" }}> | |
| 195 | 195 | -{file.deletions} |
| 196 | 196 | </Text> |
| 197 | 197 | </View> |
| @@ -204,14 +204,14 @@ export function GitCommitSheet() { · 6 unmodified lines | ||
| 204 | 204 | </View> |
| 205 | 205 | |
| 206 | 206 | <View className="gap-2"> |
| 207 | − <Text className="text-foreground text-[13px] font-t3-bold">Commit message</Text> | |
| 207 | + <Text className="text-foreground text-sm font-t3-bold">Commit message</Text> | |
| 208 | 208 | <TextInput |
| 209 | 209 | multiline |
| 210 | 210 | value={dialogCommitMessage} |
| 211 | 211 | onChangeText={setDialogCommitMessage} |
| 212 | 212 | placeholder="Leave empty to auto-generate" |
| 213 | 213 | textAlignVertical="top" |
| 214 | − className="min-h-[128px] rounded-[20px] px-4 py-3.5 font-sans text-[15px]" | |
| 214 | + className="min-h-[128px] rounded-[20px] px-4 py-3.5 font-sans text-base" | |
| 215 | 215 | style={{ |
| 216 | 216 | minHeight: 128, |
| 217 | 217 | borderWidth: 1, |
| @@ -90,15 +90,15 @@ export function GitConfirmSheet() { · 89 unmodified lines | ||
| 90 | 90 | |
| 91 | 91 | <View className="items-center gap-1 px-5 pb-3 pt-4"> |
| 92 | 92 | <Text |
| 93 | − className="text-[12px] font-t3-bold uppercase text-foreground-muted" | |
| 93 | + className="text-xs font-t3-bold uppercase text-foreground-muted" | |
| 94 | 94 | style={{ letterSpacing: 1 }} |
| 95 | 95 | > |
| 96 | 96 | Confirm |
| 97 | 97 | </Text> |
| 98 | − <Text className="text-center text-[28px] font-t3-bold"> | |
| 98 | + <Text className="text-center text-3xl font-t3-bold"> | |
| 99 | 99 | {copy?.title ?? "Run action on default branch?"} |
| 100 | 100 | </Text> |
| 101 | − <Text className="text-center text-foreground-secondary text-[13px] font-medium leading-[19px]"> | |
| 101 | + <Text className="text-center text-foreground-secondary text-sm font-medium leading-[19px]"> | |
| 102 | 102 | {copy?.description ?? "Choose how to continue."} |
| 103 | 103 | </Text> |
| 104 | 104 | </View> |
| @@ -175,13 +175,13 @@ export function GitOverviewSheet() { · 174 unmodified lines | ||
| 175 | 175 | /> |
| 176 | 176 | </Pressable> |
| 177 | 177 | <Text |
| 178 | − className="text-[12px] font-t3-bold uppercase text-foreground-muted" | |
| 178 | + className="text-xs font-t3-bold uppercase text-foreground-muted" | |
| 179 | 179 | style={{ letterSpacing: 1 }} |
| 180 | 180 | > |
| 181 | 181 | Branch |
| 182 | 182 | </Text> |
| 183 | − <Text className="text-[28px] font-t3-bold">{currentBranchLabel}</Text> | |
| 184 | − <Text className="text-foreground-secondary text-[13px] font-medium leading-[19px]"> | |
| 183 | + <Text className="text-3xl font-t3-bold">{currentBranchLabel}</Text> | |
| 184 | + <Text className="text-foreground-secondary text-sm font-medium leading-[19px]"> | |
| 185 | 185 | {statusSummary(gitStatus.data)} |
| 186 | 186 | </Text> |
| 187 | 187 | </View> |
| @@ -56,7 +56,7 @@ export function SheetActionButton(props: { · 55 unmodified lines | ||
| 56 | 56 | > |
| 57 | 57 | <SymbolView name={props.icon} size={16} tintColor={colors.textColor} type="monochrome" /> |
| 58 | 58 | <Text |
| 59 | − className="text-[12px] font-t3-bold uppercase" | |
| 59 | + className="text-xs font-t3-bold uppercase" | |
| 60 | 60 | style={{ color: colors.textColor, letterSpacing: 0.9 }} |
| 61 | 61 | > |
| 62 | 62 | {props.label} |
| @@ -69,12 +69,12 @@ export function MetaCard(props: { readonly label: string; readonly value: string · 6 unmodified lines | ||
| 69 | 69 | return ( |
| 70 | 70 | <View className="rounded-[18px] border border-border bg-card px-4 py-3"> |
| 71 | 71 | <Text |
| 72 | − className="text-foreground-muted text-[11px] font-t3-bold uppercase" | |
| 72 | + className="text-foreground-muted text-2xs font-t3-bold uppercase" | |
| 73 | 73 | style={{ letterSpacing: 0.9 }} |
| 74 | 74 | > |
| 75 | 75 | {props.label} |
| 76 | 76 | </Text> |
| 77 | − <Text selectable className="text-foreground text-[13px] font-medium" numberOfLines={1}> | |
| 77 | + <Text selectable className="text-foreground text-sm font-medium" numberOfLines={1}> | |
| 78 | 78 | {props.value} |
| 79 | 79 | </Text> |
| 80 | 80 | </View> |
| @@ -102,9 +102,9 @@ export function SheetListRow(props: { · 21 unmodified lines | ||
| 102 | 102 | <SymbolView name={props.icon} size={16} tintColor={iconColor} type="monochrome" /> |
| 103 | 103 | </View> |
| 104 | 104 | <View className="flex-1 gap-0.5"> |
| 105 | − <Text className="text-foreground text-[16px] font-t3-bold">{props.title}</Text> | |
| 105 | + <Text className="text-foreground text-base font-t3-bold">{props.title}</Text> | |
| 106 | 106 | {props.subtitle ? ( |
| 107 | − <Text className="text-foreground-muted text-[12px] leading-[17px]">{props.subtitle}</Text> | |
| 107 | + <Text className="text-foreground-muted text-xs leading-[17px]">{props.subtitle}</Text> | |
| 108 | 108 | ) : null} |
| 109 | 109 | </View> |
| 110 | 110 | <SymbolView name="chevron.right" size={13} tintColor={iconSubtleColor} type="monochrome" /> |
| @@ -98,7 +98,7 @@ export function ThreadWorkLog(props: { · 97 unmodified lines | ||
| 98 | 98 | return ( |
| 99 | 99 | <View className="-mx-1 mb-3 px-1 py-0.5"> |
| 100 | 100 | {!onlyToolRows ? ( |
| 101 | − <Text className="px-0.5 pb-0.5 font-t3-medium text-[11px] text-foreground-muted opacity-60"> | |
| 101 | + <Text className="px-0.5 pb-0.5 font-t3-medium text-2xs text-foreground-muted opacity-60"> | |
| 102 | 102 | work log |
| 103 | 103 | </Text> |
| 104 | 104 | ) : null} |
| @@ -164,7 +164,7 @@ export function ThreadWorkLog(props: { · 59 unmodified lines | ||
| 164 | 164 | |
| 165 | 165 | <View className="shrink-0 flex-row items-center gap-px"> |
| 166 | 166 | {props.copiedRowId === row.id ? ( |
| 167 | − <Text className="pr-1 font-t3-medium text-[10px] text-emerald-600 dark:text-emerald-400"> | |
| 167 | + <Text className="pr-1 font-t3-medium text-3xs text-emerald-600 dark:text-emerald-400"> | |
| 168 | 168 | Copied |
| 169 | 169 | </Text> |
| 170 | 170 | ) : null} |
| @@ -209,7 +209,7 @@ export function ThreadWorkLog(props: { · 38 unmodified lines | ||
| 209 | 209 | > |
| 210 | 210 | <Text |
| 211 | 211 | selectable |
| 212 | − className="text-[11px] leading-[17px] text-foreground-muted" | |
| 212 | + className="text-2xs leading-[17px] text-foreground-muted" | |
| 213 | 213 | style={{ fontFamily: "ui-monospace" }} |
| 214 | 214 | > |
| 215 | 215 | {row.fullDetail} |
| @@ -0,0 +1,23 @@ | ||
| 1 | +import { describe, expect, it } from "vite-plus/test"; | |
| 2 | + | |
| 3 | +import { MOBILE_CODE_SURFACE, MOBILE_TYPOGRAPHY } from "./typography"; | |
| 4 | + | |
| 5 | +describe("mobile typography", () => { | |
| 6 | + it("uses the intentional compact mobile font scale", () => { | |
| 7 | + expect(Object.values(MOBILE_TYPOGRAPHY).map(({ fontSize }) => fontSize)).toEqual([ | |
| 8 | + 10, 11, 12, 13, 14, 15, 17, 20, 24, 28, | |
| 9 | + ]); | |
| 10 | + }); | |
| 11 | + | |
| 12 | + it("uses a compact shared style for editable composer text", () => { | |
| 13 | + expect(MOBILE_TYPOGRAPHY.composer).toEqual({ fontSize: 14, lineHeight: 20 }); | |
| 14 | + }); | |
| 15 | + | |
| 16 | + it("uses caption-sized code with a compact readable row height", () => { | |
| 17 | + expect(MOBILE_CODE_SURFACE).toMatchObject({ | |
| 18 | + fontSize: MOBILE_TYPOGRAPHY.caption.fontSize, | |
| 19 | + lineNumberFontSize: MOBILE_TYPOGRAPHY.micro.fontSize, | |
| 20 | + rowHeight: 20, | |
| 21 | + }); | |
| 22 | + }); | |
| 23 | +}); | |
| @@ -0,0 +1,22 @@ | ||
| 1 | +export const MOBILE_TYPOGRAPHY = { | |
| 2 | + micro: { fontSize: 10, lineHeight: 13 }, | |
| 3 | + caption: { fontSize: 11, lineHeight: 15 }, | |
| 4 | + label: { fontSize: 12, lineHeight: 16 }, | |
| 5 | + footnote: { fontSize: 13, lineHeight: 18 }, | |
| 6 | + composer: { fontSize: 14, lineHeight: 20 }, | |
| 7 | + body: { fontSize: 15, lineHeight: 22 }, | |
| 8 | + headline: { fontSize: 17, lineHeight: 22 }, | |
| 9 | + title: { fontSize: 20, lineHeight: 26 }, | |
| 10 | + largeTitle: { fontSize: 24, lineHeight: 30 }, | |
| 11 | + display: { fontSize: 28, lineHeight: 34 }, | |
| 12 | +} as const; | |
| 13 | + | |
| 14 | +/** Shared geometry for dense, horizontally scrolling code surfaces. */ | |
| 15 | +export const MOBILE_CODE_SURFACE = { | |
| 16 | + rowHeight: 20, | |
| 17 | + gutterWidth: 46, | |
| 18 | + codePadding: 7, | |
| 19 | + textVerticalInset: 2, | |
| 20 | + fontSize: MOBILE_TYPOGRAPHY.caption.fontSize, | |
| 21 | + lineNumberFontSize: MOBILE_TYPOGRAPHY.micro.fontSize, | |
| 22 | +} as const; | |
| @@ -6,6 +6,7 @@ import { Image, StyleSheet } from "react-native"; · 5 unmodified lines | ||
| 6 | 6 | |
| 7 | 7 | import { markdownFileIconSource } from "@t3tools/mobile-markdown-text/file-icons"; |
| 8 | 8 | import { resolveMarkdownFileIcon } from "@t3tools/mobile-markdown-text/links"; |
| 9 | +import { MOBILE_TYPOGRAPHY } from "../lib/typography"; | |
| 9 | 10 | import { useThemeColor } from "../lib/useThemeColor"; |
| 10 | 11 | import type { ComposerEditorProps, ComposerEditorSelection } from "./T3ComposerEditor.types"; |
| 11 | 12 | |
| @@ -150,9 +151,15 @@ export function ComposerEditor({ · 138 unmodified lines | ||
| 150 | 151 | ? resolvedTextStyle.fontFamily |
| 151 | 152 | : "DMSans_400Regular" |
| 152 | 153 | } |
| 153 | − fontSize={typeof resolvedTextStyle.fontSize === "number" ? resolvedTextStyle.fontSize : 15} | |
| 154 | + fontSize={ | |
| 155 | + typeof resolvedTextStyle.fontSize === "number" | |
| 156 | + ? resolvedTextStyle.fontSize | |
| 157 | + : MOBILE_TYPOGRAPHY.composer.fontSize | |
| 158 | + } | |
| 154 | 159 | lineHeight={ |
| 155 | − typeof resolvedTextStyle.lineHeight === "number" ? resolvedTextStyle.lineHeight : 22 | |
| 160 | + typeof resolvedTextStyle.lineHeight === "number" | |
| 161 | + ? resolvedTextStyle.lineHeight | |
| 162 | + : MOBILE_TYPOGRAPHY.composer.lineHeight | |
| 156 | 163 | } |
| 157 | 164 | contentInsetVertical={contentInsetVertical} |
| 158 | 165 | editable={props.editable ?? true} |
| @@ -2,6 +2,7 @@ import { TextInputWrapper } from "expo-paste-input"; · 1 unmodified line | ||
| 2 | 2 | import { useImperativeHandle, useRef } from "react"; |
| 3 | 3 | import { TextInput, type TextInput as RNTextInput } from "react-native"; |
| 4 | 4 | |
| 5 | +import { MOBILE_TYPOGRAPHY } from "../lib/typography"; | |
| 5 | 6 | import { useThemeColor } from "../lib/useThemeColor"; |
| 6 | 7 | import { useNativePaste } from "../lib/useNativePaste"; |
| 7 | 8 | import type { ComposerEditorProps } from "./T3ComposerEditor.types"; |
| @@ -47,8 +48,7 @@ export function ComposerEditor({ · 39 unmodified lines | ||
| 47 | 48 | minHeight: 0, |
| 48 | 49 | color: foregroundColor, |
| 49 | 50 | fontFamily: "DMSans_400Regular", |
| 50 | − fontSize: 15, | |
| 51 | − lineHeight: 22, | |
| 51 | + ...MOBILE_TYPOGRAPHY.composer, | |
| 52 | 52 | paddingVertical: contentInsetVertical, |
| 53 | 53 | }, |
| 54 | 54 | textStyle, |
| @@ -4,27 +4,26 @@ import { expect, it } from "@effect/vitest"; · 3 unmodified lines | ||
| 4 | 4 | import * as Effect from "effect/Effect"; |
| 5 | 5 | import * as Layer from "effect/Layer"; |
| 6 | 6 | |
| 7 | −import type { ServerConfigShape } from "../config.ts"; | |
| 8 | −import { ServerConfig } from "../config.ts"; | |
| 7 | +import * as ServerConfig from "../config.ts"; | |
| 9 | 8 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; |
| 10 | 9 | import * as PairingGrantStore from "./PairingGrantStore.ts"; |
| 11 | 10 | import * as EnvironmentAuth from "./EnvironmentAuth.ts"; |
| 12 | 11 | |
| 13 | 12 | import * as ServerSecretStore from "./ServerSecretStore.ts"; |
| 14 | 13 | |
| 15 | −const makeServerConfigLayer = (overrides?: Partial<ServerConfigShape>) => | |
| 14 | +const makeServerConfigLayer = (overrides?: Partial<ServerConfig.ServerConfig["Service"]>) => | |
| 16 | 15 | Layer.effect( |
| 17 | − ServerConfig, | |
| 16 | + ServerConfig.ServerConfig, | |
| 18 | 17 | Effect.gen(function* () { |
| 19 | − const config = yield* ServerConfig; | |
| 18 | + const config = yield* ServerConfig.ServerConfig; | |
| 20 | 19 | return { |
| 21 | 20 | ...config, |
| 22 | 21 | ...overrides, |
| 23 | − } satisfies ServerConfigShape; | |
| 22 | + } satisfies ServerConfig.ServerConfig["Service"]; | |
| 24 | 23 | }), |
| 25 | 24 | ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-server-test-" }))); |
| 26 | 25 | |
| 27 | −const makeEnvironmentAuthLayer = (overrides?: Partial<ServerConfigShape>) => | |
| 26 | +const makeEnvironmentAuthLayer = (overrides?: Partial<ServerConfig.ServerConfig["Service"]>) => | |
| 28 | 27 | EnvironmentAuth.layer.pipe( |
| 29 | 28 | Layer.provide(SqlitePersistenceMemory), |
| 30 | 29 | Layer.provide(ServerSecretStore.layer), |
| @@ -33,13 +32,15 @@ const makeEnvironmentAuthLayer = (overrides?: Partial<ServerConfigShape>) => · 2 unmodified lines | ||
| 33 | 32 | |
| 34 | 33 | const makeCookieRequest = ( |
| 35 | 34 | sessionToken: string, |
| 36 | −): Parameters<EnvironmentAuth.EnvironmentAuthShape["authenticateHttpRequest"]>[0] => | |
| 35 | +): Parameters<EnvironmentAuth.EnvironmentAuth["Service"]["authenticateHttpRequest"]>[0] => | |
| 37 | 36 | ({ |
| 38 | 37 | cookies: { |
| 39 | 38 | t3_session: sessionToken, |
| 40 | 39 | }, |
| 41 | 40 | headers: {}, |
| 42 | − }) as unknown as Parameters<EnvironmentAuth.EnvironmentAuthShape["authenticateHttpRequest"]>[0]; | |
| 41 | + }) as unknown as Parameters< | |
| 42 | + EnvironmentAuth.EnvironmentAuth["Service"]["authenticateHttpRequest"] | |
| 43 | + >[0]; | |
| 43 | 44 | |
| 44 | 45 | const requestMetadata = { |
| 45 | 46 | deviceType: "desktop" as const, |
| @@ -3,31 +3,30 @@ import { expect, it } from "@effect/vitest"; · 2 unmodified lines | ||
| 3 | 3 | import * as Effect from "effect/Effect"; |
| 4 | 4 | import * as Layer from "effect/Layer"; |
| 5 | 5 | |
| 6 | −import type { ServerConfigShape } from "../config.ts"; | |
| 7 | −import { ServerConfig } from "../config.ts"; | |
| 6 | +import * as ServerConfig from "../config.ts"; | |
| 8 | 7 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; |
| 9 | 8 | import * as EnvironmentAuth from "./EnvironmentAuth.ts"; |
| 10 | 9 | import * as ServerSecretStore from "./ServerSecretStore.ts"; |
| 11 | 10 | import * as SessionStore from "./SessionStore.ts"; |
| 12 | 11 | |
| 13 | 12 | const makeServerConfigLayer = ( |
| 14 | − overrides?: Partial<Pick<ServerConfigShape, "desktopBootstrapToken">>, | |
| 13 | + overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, | |
| 15 | 14 | ) => |
| 16 | 15 | Layer.effect( |
| 17 | − ServerConfig, | |
| 16 | + ServerConfig.ServerConfig, | |
| 18 | 17 | Effect.gen(function* () { |
| 19 | − const config = yield* ServerConfig; | |
| 18 | + const config = yield* ServerConfig.ServerConfig; | |
| 20 | 19 | return { |
| 21 | 20 | ...config, |
| 22 | 21 | ...overrides, |
| 23 | − } satisfies ServerConfigShape; | |
| 22 | + } satisfies ServerConfig.ServerConfig["Service"]; | |
| 24 | 23 | }), |
| 25 | 24 | ).pipe( |
| 26 | 25 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-control-plane-test-" })), |
| 27 | 26 | ); |
| 28 | 27 | |
| 29 | 28 | const makeEnvironmentAuthLayer = ( |
| 30 | − overrides?: Partial<Pick<ServerConfigShape, "desktopBootstrapToken">>, | |
| 29 | + overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, | |
| 31 | 30 | ) => |
| 32 | 31 | EnvironmentAuth.layer.pipe( |
| 33 | 32 | Layer.provideMerge(ServerSecretStore.layer), |
| @@ -3,21 +3,22 @@ import { expect, it } from "@effect/vitest"; · 2 unmodified lines | ||
| 3 | 3 | import * as Effect from "effect/Effect"; |
| 4 | 4 | import * as Layer from "effect/Layer"; |
| 5 | 5 | |
| 6 | −import type { ServerConfigShape } from "../config.ts"; | |
| 7 | −import { ServerConfig } from "../config.ts"; | |
| 6 | +import * as ServerConfig from "../config.ts"; | |
| 8 | 7 | import * as EnvironmentAuthPolicy from "./EnvironmentAuthPolicy.ts"; |
| 9 | 8 | |
| 10 | −const makeEnvironmentAuthPolicyLayer = (overrides?: Partial<ServerConfigShape>) => | |
| 9 | +const makeEnvironmentAuthPolicyLayer = ( | |
| 10 | + overrides?: Partial<ServerConfig.ServerConfig["Service"]>, | |
| 11 | +) => | |
| 11 | 12 | EnvironmentAuthPolicy.layer.pipe( |
| 12 | 13 | Layer.provide( |
| 13 | 14 | Layer.effect( |
| 14 | − ServerConfig, | |
| 15 | + ServerConfig.ServerConfig, | |
| 15 | 16 | Effect.gen(function* () { |
| 16 | − const config = yield* ServerConfig; | |
| 17 | + const config = yield* ServerConfig.ServerConfig; | |
| 17 | 18 | return { |
| 18 | 19 | ...config, |
| 19 | 20 | ...overrides, |
| 20 | − } satisfies ServerConfigShape; | |
| 21 | + } satisfies ServerConfig.ServerConfig["Service"]; | |
| 21 | 22 | }), |
| 22 | 23 | ).pipe( |
| 23 | 24 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-policy-test-" })), |
| @@ -5,29 +5,28 @@ import * as Effect from "effect/Effect"; · 4 unmodified lines | ||
| 5 | 5 | import * as Layer from "effect/Layer"; |
| 6 | 6 | import * as TestClock from "effect/testing/TestClock"; |
| 7 | 7 | |
| 8 | −import type { ServerConfigShape } from "../config.ts"; | |
| 9 | −import { ServerConfig } from "../config.ts"; | |
| 8 | +import * as ServerConfig from "../config.ts"; | |
| 10 | 9 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; |
| 11 | 10 | import * as PairingGrantStore from "./PairingGrantStore.ts"; |
| 12 | 11 | |
| 13 | 12 | const makeServerConfigLayer = ( |
| 14 | − overrides?: Partial<Pick<ServerConfigShape, "desktopBootstrapToken">>, | |
| 13 | + overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, | |
| 15 | 14 | ) => |
| 16 | 15 | Layer.effect( |
| 17 | − ServerConfig, | |
| 16 | + ServerConfig.ServerConfig, | |
| 18 | 17 | Effect.gen(function* () { |
| 19 | − const config = yield* ServerConfig; | |
| 18 | + const config = yield* ServerConfig.ServerConfig; | |
| 20 | 19 | return { |
| 21 | 20 | ...config, |
| 22 | 21 | ...overrides, |
| 23 | − } satisfies ServerConfigShape; | |
| 22 | + } satisfies ServerConfig.ServerConfig["Service"]; | |
| 24 | 23 | }), |
| 25 | 24 | ).pipe( |
| 26 | 25 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-bootstrap-test-" })), |
| 27 | 26 | ); |
| 28 | 27 | |
| 29 | 28 | const makePairingGrantStoreLayer = ( |
| 30 | − overrides?: Partial<Pick<ServerConfigShape, "desktopBootstrapToken">>, | |
| 29 | + overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, | |
| 31 | 30 | ) => |
| 32 | 31 | PairingGrantStore.layer.pipe( |
| 33 | 32 | Layer.provide(SqlitePersistenceMemory), |
| @@ -5,30 +5,29 @@ import * as Effect from "effect/Effect"; · 4 unmodified lines | ||
| 5 | 5 | import * as Layer from "effect/Layer"; |
| 6 | 6 | import * as TestClock from "effect/testing/TestClock"; |
| 7 | 7 | |
| 8 | −import type { ServerConfigShape } from "../config.ts"; | |
| 9 | −import { ServerConfig } from "../config.ts"; | |
| 8 | +import * as ServerConfig from "../config.ts"; | |
| 10 | 9 | import { PersistenceSqlError } from "../persistence/Errors.ts"; |
| 11 | 10 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; |
| 12 | −import { AuthSessionRepository } from "../persistence/Services/AuthSessions.ts"; | |
| 11 | +import * as AuthSessions from "../persistence/Services/AuthSessions.ts"; | |
| 13 | 12 | import * as SessionStore from "./SessionStore.ts"; |
| 14 | 13 | import * as ServerSecretStore from "./ServerSecretStore.ts"; |
| 15 | 14 | |
| 16 | 15 | const makeServerConfigLayer = ( |
| 17 | − overrides?: Partial<Pick<ServerConfigShape, "desktopBootstrapToken">>, | |
| 16 | + overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, | |
| 18 | 17 | ) => |
| 19 | 18 | Layer.effect( |
| 20 | − ServerConfig, | |
| 19 | + ServerConfig.ServerConfig, | |
| 21 | 20 | Effect.gen(function* () { |
| 22 | − const config = yield* ServerConfig; | |
| 21 | + const config = yield* ServerConfig.ServerConfig; | |
| 23 | 22 | return { |
| 24 | 23 | ...config, |
| 25 | 24 | ...overrides, |
| 26 | − } satisfies ServerConfigShape; | |
| 25 | + } satisfies ServerConfig.ServerConfig["Service"]; | |
| 27 | 26 | }), |
| 28 | 27 | ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-session-test-" }))); |
| 29 | 28 | |
| 30 | 29 | const makeSessionStoreLayer = ( |
| 31 | − overrides?: Partial<Pick<ServerConfigShape, "desktopBootstrapToken">>, | |
| 30 | + overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, | |
| 32 | 31 | ) => |
| 33 | 32 | SessionStore.layer.pipe( |
| 34 | 33 | Layer.provide(SqlitePersistenceMemory), |
| @@ -41,7 +40,7 @@ const repositoryFailure = new PersistenceSqlError({ · 6 unmodified lines | ||
| 41 | 40 | detail: "sqlite is unavailable", |
| 42 | 41 | }); |
| 43 | 42 | |
| 44 | −const failingSessionLookupRepositoryLayer = Layer.succeed(AuthSessionRepository, { | |
| 43 | +const failingSessionLookupRepositoryLayer = Layer.succeed(AuthSessions.AuthSessionRepository, { | |
| 45 | 44 | create: () => Effect.void, |
| 46 | 45 | getById: () => Effect.fail(repositoryFailure), |
| 47 | 46 | listActive: () => Effect.succeed([]), |
| @@ -20,8 +20,8 @@ import * as TestConsole from "effect/testing/TestConsole"; · 19 unmodified lines | ||
| 20 | 20 | import { Command } from "effect/unstable/cli"; |
| 21 | 21 | |
| 22 | 22 | import { cli, makeCli } from "./bin.ts"; |
| 23 | −import { deriveServerPaths, ServerConfig, type ServerConfigShape } from "./config.ts"; | |
| 24 | −import { ProjectionSnapshotQuery } from "./orchestration/Services/ProjectionSnapshotQuery.ts"; | |
| 23 | +import * as ServerConfig from "./config.ts"; | |
| 24 | +import * as ProjectionSnapshotQuery from "./orchestration/Services/ProjectionSnapshotQuery.ts"; | |
| 25 | 25 | import { OrchestrationLayerLive } from "./orchestration/runtimeLayer.ts"; |
| 26 | 26 | import { orchestrationHttpApiLayer } from "./orchestration/http.ts"; |
| 27 | 27 | import { layerConfig as SqlitePersistenceLayerLive } from "./persistence/Layers/Sqlite.ts"; |
| @@ -57,7 +57,7 @@ const captureStdout = <A, E, R>(effect: Effect.Effect<A, E, R>) => · 29 unmodified lines | ||
| 57 | 57 | |
| 58 | 58 | const makeCliTestServerConfig = (baseDir: string) => |
| 59 | 59 | Effect.gen(function* () { |
| 60 | − const derivedPaths = yield* deriveServerPaths(baseDir, undefined); | |
| 60 | + const derivedPaths = yield* ServerConfig.deriveServerPaths(baseDir, undefined); | |
| 61 | 61 | return { |
| 62 | 62 | logLevel: "Info", |
| 63 | 63 | traceMinLevel: "Info", |
| @@ -84,26 +84,23 @@ const makeCliTestServerConfig = (baseDir: string) => · 20 unmodified lines | ||
| 84 | 84 | logWebSocketEvents: false, |
| 85 | 85 | tailscaleServeEnabled: false, |
| 86 | 86 | tailscaleServePort: 443, |
| 87 | − } satisfies ServerConfigShape; | |
| 87 | + } satisfies ServerConfig.ServerConfig["Service"]; | |
| 88 | 88 | }); |
| 89 | 89 | |
| 90 | −const makeProjectPersistenceLayer = (config: ServerConfigShape) => | |
| 90 | +const makeProjectPersistenceLayer = (config: ServerConfig.ServerConfig["Service"]) => | |
| 91 | 91 | Layer.mergeAll( |
| 92 | 92 | OrchestrationLayerLive.pipe( |
| 93 | 93 | Layer.provideMerge(RepositoryIdentityResolverLive), |
| 94 | 94 | Layer.provideMerge(SqlitePersistenceLayerLive), |
| 95 | 95 | ), |
| 96 | 96 | WorkspacePathsLive, |
| 97 | − ).pipe( | |
| 98 | − Layer.provideMerge(NodeServices.layer), | |
| 99 | − Layer.provide(Layer.succeed(ServerConfig, config)), | |
| 100 | − ); | |
| 97 | + ).pipe(Layer.provideMerge(NodeServices.layer), Layer.provide(ServerConfig.layer(config))); | |
| 101 | 98 | |
| 102 | 99 | const readPersistedSnapshot = (baseDir: string) => |
| 103 | 100 | Effect.gen(function* () { |
| 104 | 101 | const config = yield* makeCliTestServerConfig(baseDir); |
| 105 | 102 | return yield* Effect.gen(function* () { |
| 106 | − const projectionSnapshotQuery = yield* ProjectionSnapshotQuery; | |
| 103 | + const projectionSnapshotQuery = yield* ProjectionSnapshotQuery.ProjectionSnapshotQuery; | |
| 107 | 104 | return yield* projectionSnapshotQuery.getSnapshot(); |
| 108 | 105 | }).pipe(Effect.provide(makeProjectPersistenceLayer(config))); |
| 109 | 106 | }); |
| @@ -133,7 +130,7 @@ const withLiveProjectCliServer = <A, E, R>(baseDir: string, run: () => Effect.Ef · 23 unmodified lines | ||
| 133 | 130 | }), |
| 134 | 131 | ), |
| 135 | 132 | Layer.provideMerge(NodeServices.layer), |
| 136 | − Layer.provide(Layer.succeed(ServerConfig, config)), | |
| 133 | + Layer.provide(ServerConfig.layer(config)), | |
| 137 | 134 | ); |
| 138 | 135 | |
| 139 | 136 | return yield* Effect.scoped( |
| @@ -238,7 +235,7 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { · 98 unmodified lines | ||
| 238 | 235 | it.effect("logs in to headless connect without enabling access", () => |
| 239 | 236 | Effect.gen(function* () { |
| 240 | 237 | const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-cloud-login-test-")); |
| 241 | − const { secretsDir } = yield* deriveServerPaths(baseDir, undefined); | |
| 238 | + const { secretsDir } = yield* ServerConfig.deriveServerPaths(baseDir, undefined); | |
| 242 | 239 | mkdirSync(secretsDir, { recursive: true }); |
| 243 | 240 | writeFileSync( |
| 244 | 241 | join(secretsDir, "cloud-cli-oauth-token.bin"), |
| @@ -282,7 +279,7 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { · 37 unmodified lines | ||
| 282 | 279 | it.effect("logs out of headless connect and removes the stored CLI authorization", () => |
| 283 | 280 | Effect.gen(function* () { |
| 284 | 281 | const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-cloud-logout-test-")); |
| 285 | − const { secretsDir } = yield* deriveServerPaths(baseDir, undefined); | |
| 282 | + const { secretsDir } = yield* ServerConfig.deriveServerPaths(baseDir, undefined); | |
| 286 | 283 | const tokenPath = join(secretsDir, "cloud-cli-oauth-token.bin"); |
| 287 | 284 | mkdirSync(secretsDir, { recursive: true }); |
| 288 | 285 | writeFileSync(tokenPath, "invalid persisted token"); |
| @@ -461,7 +458,7 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { · 172 unmodified lines | ||
| 461 | 458 | "--base-dir", |
| 462 | 459 | baseDir, |
| 463 | 460 | ]); |
| 464 | − const projectionSnapshotQuery = yield* ProjectionSnapshotQuery; | |
| 461 | + const projectionSnapshotQuery = yield* ProjectionSnapshotQuery.ProjectionSnapshotQuery; | |
| 465 | 462 | const readModel = yield* projectionSnapshotQuery.getSnapshot(); |
| 466 | 463 | const addedProject = readModel.projects.find( |
| 467 | 464 | (project) => project.workspaceRoot === workspaceRoot && project.deletedAt === null, |
| @@ -18,7 +18,7 @@ import { · 17 unmodified lines | ||
| 18 | 18 | formatPairingCredentialList, |
| 19 | 19 | formatSessionList, |
| 20 | 20 | } from "../cliAuthFormat.ts"; |
| 21 | −import { ServerConfig } from "../config.ts"; | |
| 21 | +import * as ServerConfig from "../config.ts"; | |
| 22 | 22 | import { |
| 23 | 23 | authLocationFlags, |
| 24 | 24 | type CliAuthLocationFlags, |
| @@ -28,7 +28,7 @@ import { · 3 unmodified lines | ||
| 28 | 28 | |
| 29 | 29 | const runWithEnvironmentAuth = <A, E>( |
| 30 | 30 | flags: CliAuthLocationFlags, |
| 31 | − run: (environmentAuth: EnvironmentAuth.EnvironmentAuthShape) => Effect.Effect<A, E>, | |
| 31 | + run: (environmentAuth: EnvironmentAuth.EnvironmentAuth["Service"]) => Effect.Effect<A, E>, | |
| 32 | 32 | options?: { |
| 33 | 33 | readonly quietLogs?: boolean; |
| 34 | 34 | }, |
| @@ -43,7 +43,7 @@ const runWithEnvironmentAuth = <A, E>( · 8 unmodified lines | ||
| 43 | 43 | }).pipe( |
| 44 | 44 | Effect.provide( |
| 45 | 45 | Layer.mergeAll(EnvironmentAuth.runtimeLayer).pipe( |
| 46 | − Layer.provide(Layer.succeed(ServerConfig, config)), | |
| 46 | + Layer.provide(ServerConfig.layer(config)), | |
| 47 | 47 | Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)), |
| 48 | 48 | ), |
| 49 | 49 | ), |
| @@ -14,18 +14,10 @@ import * as SchemaTransformation from "effect/SchemaTransformation"; · 13 unmodified lines | ||
| 14 | 14 | import { Argument, Flag } from "effect/unstable/cli"; |
| 15 | 15 | |
| 16 | 16 | import { readBootstrapEnvelope } from "../bootstrap.ts"; |
| 17 | −import { | |
| 18 | − DEFAULT_PORT, | |
| 19 | − deriveServerPaths, | |
| 20 | − ensureServerDirectories, | |
| 21 | − resolveStaticDir, | |
| 22 | − RuntimeMode, | |
| 23 | − type ServerConfigShape, | |
| 24 | − type StartupPresentation, | |
| 25 | −} from "../config.ts"; | |
| 17 | +import * as ServerConfig from "../config.ts"; | |
| 26 | 18 | import { expandHomePath, resolveBaseDir } from "../os-jank.ts"; |
| 27 | 19 | |
| 28 | −export const modeFlag = Flag.choice("mode", RuntimeMode.literals).pipe( | |
| 20 | +export const modeFlag = Flag.choice("mode", ServerConfig.RuntimeMode.literals).pipe( | |
| 29 | 21 | Flag.withDescription("Runtime mode. `desktop` keeps loopback defaults unless overridden."), |
| 30 | 22 | Flag.optional, |
| 31 | 23 | ); |
| @@ -104,7 +96,7 @@ const EnvServerConfig = Config.all({ · 72 unmodified lines | ||
| 104 | 96 | Config.withDefault(10_000), |
| 105 | 97 | ), |
| 106 | 98 | otlpServiceName: Config.string("T3CODE_OTLP_SERVICE_NAME").pipe(Config.withDefault("t3-server")), |
| 107 | − mode: Config.schema(RuntimeMode, "T3CODE_MODE").pipe( | |
| 99 | + mode: Config.schema(ServerConfig.RuntimeMode, "T3CODE_MODE").pipe( | |
| 108 | 100 | Config.option, |
| 109 | 101 | Config.map(Option.getOrUndefined), |
| 110 | 102 | ), |
| @@ -139,7 +131,7 @@ const EnvServerConfig = Config.all({ · 28 unmodified lines | ||
| 139 | 131 | }); |
| 140 | 132 | |
| 141 | 133 | export interface CliServerFlags { |
| 142 | − readonly mode: Option.Option<RuntimeMode>; | |
| 134 | + readonly mode: Option.Option<ServerConfig.RuntimeMode>; | |
| 143 | 135 | readonly port: Option.Option<number>; |
| 144 | 136 | readonly host: Option.Option<string>; |
| 145 | 137 | readonly baseDir: Option.Option<string>; |
| @@ -208,7 +200,7 @@ export const resolveServerConfig = ( · 62 unmodified lines | ||
| 208 | 200 | flags: CliServerFlags, |
| 209 | 201 | cliLogLevel: Option.Option<LogLevel.LogLevel>, |
| 210 | 202 | options?: { |
| 211 | − readonly startupPresentation?: StartupPresentation; | |
| 203 | + readonly startupPresentation?: ServerConfig.StartupPresentation; | |
| 212 | 204 | readonly forceAutoBootstrapProjectFromCwd?: boolean; |
| 213 | 205 | }, |
| 214 | 206 | ) => |
| @@ -238,7 +230,7 @@ export const resolveServerConfig = ( · 23 unmodified lines | ||
| 238 | 230 | : Option.none(); |
| 239 | 231 | const bootstrap = Option.getOrUndefined(bootstrapEnvelope); |
| 240 | 232 | |
| 241 | − const mode: RuntimeMode = Option.getOrElse( | |
| 233 | + const mode: ServerConfig.RuntimeMode = Option.getOrElse( | |
| 242 | 234 | resolveOptionPrecedence( |
| 243 | 235 | normalizedFlags.mode, |
| 244 | 236 | Option.fromUndefinedOr(env.mode), |
| @@ -257,9 +249,9 @@ export const resolveServerConfig = ( · 12 unmodified lines | ||
| 257 | 249 | onSome: (value) => Effect.succeed(value), |
| 258 | 250 | onNone: () => { |
| 259 | 251 | if (mode === "desktop") { |
| 260 | − return Effect.succeed(DEFAULT_PORT); | |
| 252 | + return Effect.succeed(ServerConfig.DEFAULT_PORT); | |
| 261 | 253 | } |
| 262 | − return findAvailablePort(DEFAULT_PORT); | |
| 254 | + return findAvailablePort(ServerConfig.DEFAULT_PORT); | |
| 263 | 255 | }, |
| 264 | 256 | }, |
| 265 | 257 | ); |
| @@ -279,8 +271,8 @@ export const resolveServerConfig = ( · 13 unmodified lines | ||
| 279 | 271 | const rawCwd = Option.getOrElse(normalizedFlags.cwd, () => process.cwd()); |
| 280 | 272 | const cwd = path.resolve(yield* expandHomePath(rawCwd.trim())); |
| 281 | 273 | yield* fs.makeDirectory(cwd, { recursive: true }); |
| 282 | − const derivedPaths = yield* deriveServerPaths(baseDir, devUrl); | |
| 283 | − yield* ensureServerDirectories(derivedPaths); | |
| 274 | + const derivedPaths = yield* ServerConfig.deriveServerPaths(baseDir, devUrl); | |
| 275 | + yield* ServerConfig.ensureServerDirectories(derivedPaths); | |
| 284 | 276 | const persistedObservabilitySettings = yield* loadPersistedObservabilitySettings( |
| 285 | 277 | derivedPaths.settingsPath, |
| 286 | 278 | ); |
| @@ -330,7 +322,7 @@ export const resolveServerConfig = ( · 43 unmodified lines | ||
| 330 | 322 | ), |
| 331 | 323 | () => 443, |
| 332 | 324 | ); |
| 333 | − const staticDir = devUrl ? undefined : yield* resolveStaticDir(); | |
| 325 | + const staticDir = devUrl ? undefined : yield* ServerConfig.resolveStaticDir(); | |
| 334 | 326 | const host = Option.getOrElse( |
| 335 | 327 | resolveOptionPrecedence( |
| 336 | 328 | normalizedFlags.host, |
| @@ -341,7 +333,7 @@ export const resolveServerConfig = ( · 4 unmodified lines | ||
| 341 | 333 | ); |
| 342 | 334 | const logLevel = Option.getOrElse(cliLogLevel, () => env.logLevel); |
| 343 | 335 | |
| 344 | − const config: ServerConfigShape = { | |
| 336 | + const config: ServerConfig.ServerConfig["Service"] = { | |
| 345 | 337 | logLevel, |
| 346 | 338 | traceMinLevel: env.traceMinLevel, |
| 347 | 339 | traceTimingEnabled: env.traceTimingEnabled, |
| @@ -31,9 +31,9 @@ import * as CliTokenManager from "../cloud/CliTokenManager.ts"; · 30 unmodified lines | ||
| 31 | 31 | import { CLOUD_LINKED_USER_ID, RELAY_URL_SECRET } from "../cloud/config.ts"; |
| 32 | 32 | import { relayUrlConfig } from "../cloud/publicConfig.ts"; |
| 33 | 33 | import { headlessRelayClientTracingLayer } from "../cloud/relayTracing.ts"; |
| 34 | −import { ServerConfig } from "../config.ts"; | |
| 34 | +import * as ServerConfig from "../config.ts"; | |
| 35 | 35 | import { ServerEnvironmentLive } from "../environment/Layers/ServerEnvironment.ts"; |
| 36 | −import { ServerEnvironment } from "../environment/Services/ServerEnvironment.ts"; | |
| 36 | +import * as ServerEnvironment from "../environment/Services/ServerEnvironment.ts"; | |
| 37 | 37 | import { readPersistedServerRuntimeState } from "../serverRuntimeState.ts"; |
| 38 | 38 | import { projectLocationFlags, resolveCliAuthConfig } from "./config.ts"; |
| 39 | 39 | |
| @@ -145,7 +145,7 @@ const reportRelayClientInstallProgress = (event: RelayClientInstallProgressEvent · 105 unmodified lines | ||
| 145 | 145 | |
| 146 | 146 | export const acquireRelayClientForLink = Effect.fn("cloud.cli.acquire_relay_client_for_link")( |
| 147 | 147 | function* <ConfirmError, ConfirmContext>( |
| 148 | − relayClient: RelayClient.RelayClientShape, | |
| 148 | + relayClient: RelayClient.RelayClient["Service"], | |
| 149 | 149 | confirmInstall: (version: string) => Effect.Effect<boolean, ConfirmError, ConfirmContext>, |
| 150 | 150 | reportProgress: (event: RelayClientInstallProgressEvent) => Effect.Effect<void>, |
| 151 | 151 | ) { |
| @@ -164,7 +164,7 @@ export const acquireRelayClientForLink = Effect.fn("cloud.cli.acquire_relay_clie · 12 unmodified lines | ||
| 164 | 164 | ); |
| 165 | 165 | |
| 166 | 166 | const withCloudCliSessionToken = <A, E, R>( |
| 167 | − environmentAuth: EnvironmentAuth.EnvironmentAuthShape, | |
| 167 | + environmentAuth: EnvironmentAuth.EnvironmentAuth["Service"], | |
| 168 | 168 | run: (token: string) => Effect.Effect<A, E, R>, |
| 169 | 169 | ) => |
| 170 | 170 | Effect.acquireUseRelease( |
| @@ -183,7 +183,7 @@ type LiveCloudActionResult = · 12 unmodified lines | ||
| 183 | 183 | | { readonly status: "failed"; readonly cause: unknown }; |
| 184 | 184 | |
| 185 | 185 | const runLiveCloudUnlink = Effect.fn("cloud.cli.run_live_unlink")(function* () { |
| 186 | − const config = yield* ServerConfig; | |
| 186 | + const config = yield* ServerConfig.ServerConfig; | |
| 187 | 187 | const runtimeState = yield* readPersistedServerRuntimeState(config.serverRuntimeStatePath); |
| 188 | 188 | if (Option.isNone(runtimeState)) { |
| 189 | 189 | return { status: "not-running" } satisfies LiveCloudActionResult; |
| @@ -219,7 +219,7 @@ const unlinkRelayEnvironment = Effect.fn("cloud.cli.unlink_relay_environment")(f · 29 unmodified lines | ||
| 219 | 219 | return { status: "not-authenticated" } satisfies RelayUnlinkResult; |
| 220 | 220 | } |
| 221 | 221 | |
| 222 | − const environment = yield* ServerEnvironment; | |
| 222 | + const environment = yield* ServerEnvironment.ServerEnvironment; | |
| 223 | 223 | const environmentId = yield* environment.getEnvironmentId; |
| 224 | 224 | const relayUrl = yield* relayUrlConfig; |
| 225 | 225 | const httpClient = yield* HttpClient.HttpClient; |
| @@ -285,8 +285,8 @@ const runCloudCommand = <A, E>( · 59 unmodified lines | ||
| 285 | 285 | | FileSystem.FileSystem |
| 286 | 286 | | HttpClient.HttpClient |
| 287 | 287 | | Prompt.Environment |
| 288 | − | ServerConfig | |
| 289 | − | ServerEnvironment | |
| 288 | + | ServerConfig.ServerConfig | |
| 289 | + | ServerEnvironment.ServerEnvironment | |
| 290 | 290 | >, |
| 291 | 291 | options?: { |
| 292 | 292 | readonly quietLogs?: boolean; |
| @@ -305,7 +305,7 @@ const runCloudCommand = <A, E>( · 12 unmodified lines | ||
| 305 | 305 | headlessRelayClientTracingLayer, |
| 306 | 306 | ).pipe( |
| 307 | 307 | Layer.provideMerge(FetchHttpClient.layer), |
| 308 | − Layer.provideMerge(Layer.succeed(ServerConfig, config)), | |
| 308 | + Layer.provideMerge(ServerConfig.layer(config)), | |
| 309 | 309 | Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)), |
| 310 | 310 | ); |
| 311 | 311 | return yield* run.pipe(Effect.provide(runtimeLayer)); |
| @@ -26,19 +26,19 @@ import * as HttpApiClient from "effect/unstable/httpapi/HttpApiClient"; · 25 unmodified lines | ||
| 26 | 26 | |
| 27 | 27 | import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts"; |
| 28 | 28 | |
| 29 | −import { ServerConfig, type ServerConfigShape } from "../config.ts"; | |
| 30 | −import { OrchestrationEngineService } from "../orchestration/Services/OrchestrationEngine.ts"; | |
| 31 | −import { ProjectionSnapshotQuery } from "../orchestration/Services/ProjectionSnapshotQuery.ts"; | |
| 29 | +import * as ServerConfig from "../config.ts"; | |
| 30 | +import * as OrchestrationEngine from "../orchestration/Services/OrchestrationEngine.ts"; | |
| 31 | +import * as ProjectionSnapshotQuery from "../orchestration/Services/ProjectionSnapshotQuery.ts"; | |
| 32 | 32 | import { OrchestrationLayerLive } from "../orchestration/runtimeLayer.ts"; |
| 33 | 33 | import { layerConfig as SqlitePersistenceLayerLive } from "../persistence/Layers/Sqlite.ts"; |
| 34 | 34 | import { RepositoryIdentityResolverLive } from "../project/Layers/RepositoryIdentityResolver.ts"; |
| 35 | −import { getAutoBootstrapDefaultModelSelection } from "../serverRuntimeStartup.ts"; | |
| 35 | +import * as ServerRuntimeStartup from "../serverRuntimeStartup.ts"; | |
| 36 | 36 | import { |
| 37 | 37 | clearPersistedServerRuntimeState, |
| 38 | 38 | readPersistedServerRuntimeState, |
| 39 | 39 | } from "../serverRuntimeState.ts"; |
| 40 | 40 | import { WorkspacePathsLive } from "../workspace/Layers/WorkspacePaths.ts"; |
| 41 | −import { WorkspacePaths } from "../workspace/Services/WorkspacePaths.ts"; | |
| 41 | +import * as WorkspacePaths from "../workspace/Services/WorkspacePaths.ts"; | |
| 42 | 42 | import { type CliAuthLocationFlags, projectLocationFlags, resolveCliAuthConfig } from "./config.ts"; |
| 43 | 43 | |
| 44 | 44 | type ProjectMutationTarget = { |
| @@ -78,7 +78,7 @@ const ProjectCliRuntimeLive = Layer.mergeAll( · 33 unmodified lines | ||
| 78 | 78 | const PROJECT_CLI_LIVE_SERVER_TIMEOUT = Duration.seconds(1); |
| 79 | 79 | const isEnvironmentHttpCommonError = Schema.is(EnvironmentHttpCommonError); |
| 80 | 80 | const withProjectCliSessionToken = <A, E, R>( |
| 81 | − environmentAuth: EnvironmentAuth.EnvironmentAuthShape, | |
| 81 | + environmentAuth: EnvironmentAuth.EnvironmentAuth["Service"], | |
| 82 | 82 | run: (token: string) => Effect.Effect<A, E, R>, |
| 83 | 83 | ) => |
| 84 | 84 | Effect.acquireUseRelease( |
| @@ -123,7 +123,7 @@ const makeLiveServerClient = (origin: string) => · 38 unmodified lines | ||
| 123 | 123 | const normalizeWorkspaceRootForProjectCommand = Effect.fn( |
| 124 | 124 | "normalizeWorkspaceRootForProjectCommand", |
| 125 | 125 | )(function* (workspaceRoot: string) { |
| 126 | − const workspacePaths = yield* WorkspacePaths; | |
| 126 | + const workspacePaths = yield* WorkspacePaths.WorkspacePaths; | |
| 127 | 127 | return yield* workspacePaths.normalizeWorkspaceRoot(workspaceRoot); |
| 128 | 128 | }); |
| 129 | 129 | |
| @@ -211,12 +211,15 @@ const dispatchLiveOrchestrationCommand = ( · 81 unmodified lines | ||
| 211 | 211 | }).pipe(withProjectCliLiveServerTimeout, Effect.catch(failLiveServerRequest)); |
| 212 | 212 | |
| 213 | 213 | const getOfflineSnapshot = Effect.fn("getOfflineSnapshot")(function* () { |
| 214 | − const projectionSnapshotQuery = yield* ProjectionSnapshotQuery; | |
| 214 | + const projectionSnapshotQuery = yield* ProjectionSnapshotQuery.ProjectionSnapshotQuery; | |
| 215 | 215 | return yield* projectionSnapshotQuery.getSnapshot(); |
| 216 | 216 | }); |
| 217 | 217 | |
| 218 | 218 | const tryResolveLiveProjectExecutionMode = Effect.fn("tryResolveLiveProjectExecutionMode")( |
| 219 | − function* (environmentAuth: EnvironmentAuth.EnvironmentAuthShape, config: ServerConfigShape) { | |
| 219 | + function* ( | |
| 220 | + environmentAuth: EnvironmentAuth.EnvironmentAuth["Service"], | |
| 221 | + config: ServerConfig.ServerConfig["Service"], | |
| 222 | + ) { | |
| 220 | 223 | const runtimeState = yield* readPersistedServerRuntimeState(config.serverRuntimeStatePath); |
| 221 | 224 | if (Option.isNone(runtimeState)) { |
| 222 | 225 | return Option.none<{ readonly origin: string }>(); |
| @@ -251,7 +254,11 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* ( · 28 unmodified lines | ||
| 251 | 254 | }) => Effect.Effect< |
| 252 | 255 | string, |
| 253 | 256 | Error, |
| 254 | − Crypto.Crypto | FileSystem.FileSystem | HttpClient.HttpClient | Path.Path | WorkspacePaths | |
| 257 | + | Crypto.Crypto | |
| 258 | + | FileSystem.FileSystem | |
| 259 | + | HttpClient.HttpClient | |
| 260 | + | Path.Path | |
| 261 | + | WorkspacePaths.WorkspacePaths | |
| 255 | 262 | >, |
| 256 | 263 | ) { |
| 257 | 264 | const logLevel = yield* GlobalFlag.LogLevel; |
| @@ -278,13 +285,13 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* ( · 20 unmodified lines | ||
| 278 | 285 | } |
| 279 | 286 | |
| 280 | 287 | const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe( |
| 281 | − Layer.provide(Layer.succeed(ServerConfig, config)), | |
| 288 | + Layer.provide(ServerConfig.layer(config)), | |
| 282 | 289 | Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)), |
| 283 | 290 | ); |
| 284 | 291 | |
| 285 | 292 | return yield* Effect.gen(function* () { |
| 286 | 293 | const snapshot = yield* getOfflineSnapshot(); |
| 287 | − const orchestrationEngine = yield* OrchestrationEngineService; | |
| 294 | + const orchestrationEngine = yield* OrchestrationEngine.OrchestrationEngineService; | |
| 288 | 295 | const output = yield* run({ |
| 289 | 296 | snapshot, |
| 290 | 297 | dispatch: (command) => orchestrationEngine.dispatch(command), |
| @@ -296,7 +303,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* ( · 5 unmodified lines | ||
| 296 | 303 | Effect.provide( |
| 297 | 304 | Layer.mergeAll(EnvironmentAuth.runtimeLayer, WorkspacePathsLive).pipe( |
| 298 | 305 | Layer.provideMerge(FetchHttpClient.layer), |
| 299 | − Layer.provide(Layer.succeed(ServerConfig, config)), | |
| 306 | + Layer.provide(ServerConfig.layer(config)), | |
| 300 | 307 | Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)), |
| 301 | 308 | ), |
| 302 | 309 | ), |
| @@ -341,7 +348,7 @@ const projectAddCommand = Command.make("add", { · 38 unmodified lines | ||
| 341 | 348 | projectId, |
| 342 | 349 | title, |
| 343 | 350 | workspaceRoot, |
| 344 | − defaultModelSelection: getAutoBootstrapDefaultModelSelection(), | |
| 351 | + defaultModelSelection: ServerRuntimeStartup.getAutoBootstrapDefaultModelSelection(), | |
| 345 | 352 | createdAt: DateTime.formatIso(yield* DateTime.now), |
| 346 | 353 | }); |
| 347 | 354 | return `Added project ${projectId} (${title}) at ${workspaceRoot}.`; |
| @@ -6,13 +6,13 @@ · 5 unmodified lines | ||
| 6 | 6 | * |
| 7 | 7 | * @module ServerConfig |
| 8 | 8 | */ |
| 9 | +import * as Context from "effect/Context"; | |
| 9 | 10 | import * as Effect from "effect/Effect"; |
| 10 | 11 | import * as FileSystem from "effect/FileSystem"; |
| 11 | 12 | import * as Layer from "effect/Layer"; |
| 12 | 13 | import * as LogLevel from "effect/LogLevel"; |
| 13 | 14 | import * as Path from "effect/Path"; |
| 14 | 15 | import * as Schema from "effect/Schema"; |
| 15 | −import * as Context from "effect/Context"; | |
| 16 | 16 | |
| 17 | 17 | export const DEFAULT_PORT = 3773; |
| 18 | 18 | |
| @@ -46,38 +46,51 @@ export interface ServerDerivedPaths { · 27 unmodified lines | ||
| 46 | 46 | } |
| 47 | 47 | |
| 48 | 48 | /** |
| 49 | − * ServerConfigShape - Process/runtime configuration required by the server. | |
| 49 | + * ServerConfig - Service tag for server runtime configuration. | |
| 50 | 50 | */ |
| 51 | −export interface ServerConfigShape extends ServerDerivedPaths { | |
| 52 | − readonly logLevel: LogLevel.LogLevel; | |
| 53 | − readonly traceMinLevel: LogLevel.LogLevel; | |
| 54 | − readonly traceTimingEnabled: boolean; | |
| 55 | − readonly traceBatchWindowMs: number; | |
| 56 | − readonly traceMaxBytes: number; | |
| 57 | − readonly traceMaxFiles: number; | |
| 58 | − readonly otlpTracesUrl: string | undefined; | |
| 59 | − readonly otlpMetricsUrl: string | undefined; | |
| 60 | − readonly otlpExportIntervalMs: number; | |
| 61 | − readonly otlpServiceName: string; | |
| 62 | − readonly mode: RuntimeMode; | |
| 63 | − readonly port: number; | |
| 64 | − readonly host: string | undefined; | |
| 65 | − readonly cwd: string; | |
| 66 | − readonly baseDir: string; | |
| 67 | − readonly staticDir: string | undefined; | |
| 68 | − readonly devUrl: URL | undefined; | |
| 69 | − readonly noBrowser: boolean; | |
| 70 | − readonly startupPresentation: StartupPresentation; | |
| 71 | − readonly desktopBootstrapToken: string | undefined; | |
| 72 | − readonly autoBootstrapProjectFromCwd: boolean; | |
| 73 | − readonly logWebSocketEvents: boolean; | |
| 74 | − readonly tailscaleServeEnabled: boolean; | |
| 75 | − readonly tailscaleServePort: number; | |
| 51 | +export class ServerConfig extends Context.Service< | |
| 52 | + ServerConfig, | |
| 53 | + ServerDerivedPaths & { | |
| 54 | + readonly logLevel: LogLevel.LogLevel; | |
| 55 | + readonly traceMinLevel: LogLevel.LogLevel; | |
| 56 | + readonly traceTimingEnabled: boolean; | |
| 57 | + readonly traceBatchWindowMs: number; | |
| 58 | + readonly traceMaxBytes: number; | |
| 59 | + readonly traceMaxFiles: number; | |
| 60 | + readonly otlpTracesUrl: string | undefined; | |
| 61 | + readonly otlpMetricsUrl: string | undefined; | |
| 62 | + readonly otlpExportIntervalMs: number; | |
| 63 | + readonly otlpServiceName: string; | |
| 64 | + readonly mode: RuntimeMode; | |
| 65 | + readonly port: number; | |
| 66 | + readonly host: string | undefined; | |
| 67 | + readonly cwd: string; | |
| 68 | + readonly baseDir: string; | |
| 69 | + readonly staticDir: string | undefined; | |
| 70 | + readonly devUrl: URL | undefined; | |
| 71 | + readonly noBrowser: boolean; | |
| 72 | + readonly startupPresentation: StartupPresentation; | |
| 73 | + readonly desktopBootstrapToken: string | undefined; | |
| 74 | + readonly autoBootstrapProjectFromCwd: boolean; | |
| 75 | + readonly logWebSocketEvents: boolean; | |
| 76 | + readonly tailscaleServeEnabled: boolean; | |
| 77 | + readonly tailscaleServePort: number; | |
| 78 | + } | |
| 79 | +>()("t3/config/ServerConfig") { | |
| 80 | + /** @deprecated Import and use `layerTest` from this module. */ | |
| 81 | + static readonly layerTest = ( | |
| 82 | + cwd: string, | |
| 83 | + baseDirOrPrefix: string | { readonly prefix: string }, | |
| 84 | + ) => layerTest(cwd, baseDirOrPrefix); | |
| 76 | 85 | } |
| 77 | 86 | |
| 87 | +export const make = (config: ServerConfig["Service"]) => ServerConfig.of(config); | |
| 88 | + | |
| 89 | +export const layer = (config: ServerConfig["Service"]) => Layer.succeed(ServerConfig, make(config)); | |
| 90 | + | |
| 78 | 91 | export const deriveServerPaths = Effect.fn(function* ( |
| 79 | − baseDir: ServerConfigShape["baseDir"], | |
| 80 | − devUrl: ServerConfigShape["devUrl"], | |
| 92 | + baseDir: ServerConfig["Service"]["baseDir"], | |
| 93 | + devUrl: ServerConfig["Service"]["devUrl"], | |
| 81 | 94 | ): Effect.fn.Return<ServerDerivedPaths, never, Path.Path> { |
| 82 | 95 | const { join } = yield* Path.Path; |
| 83 | 96 | const stateDir = join(baseDir, devUrl !== undefined ? "dev" : "userdata"); |
| @@ -129,56 +142,50 @@ export const ensureServerDirectories = Effect.fn(function* (derivedPaths: Server · 45 unmodified lines | ||
| 129 | 142 | ); |
| 130 | 143 | }); |
| 131 | 144 | |
| 132 | −/** | |
| 133 | − * ServerConfig - Service tag for server runtime configuration. | |
| 134 | − */ | |
| 135 | −export class ServerConfig extends Context.Service<ServerConfig, ServerConfigShape>()( | |
| 136 | − "t3/config/ServerConfig", | |
| 145 | +const makeTest = Effect.fn("ServerConfig.makeTest")(function* ( | |
| 146 | + cwd: string, | |
| 147 | + baseDirOrPrefix: string | { readonly prefix: string }, | |
| 137 | 148 | ) { |
| 138 | − static readonly layerTest = (cwd: string, baseDirOrPrefix: string | { prefix: string }) => | |
| 139 | − Layer.effect( | |
| 140 | − ServerConfig, | |
| 141 | − Effect.gen(function* () { | |
| 142 | − const devUrl = undefined; | |
| 149 | + const devUrl = undefined; | |
| 150 | + const fs = yield* FileSystem.FileSystem; | |
| 151 | + const baseDir = | |
| 152 | + typeof baseDirOrPrefix === "string" | |
| 153 | + ? baseDirOrPrefix | |
| 154 | + : yield* fs.makeTempDirectoryScoped({ prefix: baseDirOrPrefix.prefix }); | |
| 155 | + const derivedPaths = yield* deriveServerPaths(baseDir, devUrl); | |
| 156 | + yield* ensureServerDirectories(derivedPaths); | |
| 143 | 157 | |
| 144 | − const fs = yield* FileSystem.FileSystem; | |
| 145 | − const baseDir = | |
| 146 | − typeof baseDirOrPrefix === "string" | |
| 147 | − ? baseDirOrPrefix | |
| 148 | − : yield* fs.makeTempDirectoryScoped({ prefix: baseDirOrPrefix.prefix }); | |
| 149 | − const derivedPaths = yield* deriveServerPaths(baseDir, devUrl); | |
| 150 | − yield* ensureServerDirectories(derivedPaths); | |
| 158 | + return ServerConfig.of({ | |
| 159 | + logLevel: "Error", | |
| 160 | + traceMinLevel: "Info", | |
| 161 | + traceTimingEnabled: true, | |
| 162 | + traceBatchWindowMs: 200, | |
| 163 | + traceMaxBytes: 10 * 1024 * 1024, | |
| 164 | + traceMaxFiles: 10, | |
| 165 | + otlpTracesUrl: undefined, | |
| 166 | + otlpMetricsUrl: undefined, | |
| 167 | + otlpExportIntervalMs: 10_000, | |
| 168 | + otlpServiceName: "t3-server", | |
| 169 | + cwd, | |
| 170 | + baseDir, | |
| 171 | + ...derivedPaths, | |
| 172 | + mode: "web", | |
| 173 | + autoBootstrapProjectFromCwd: false, | |
| 174 | + logWebSocketEvents: false, | |
| 175 | + tailscaleServeEnabled: false, | |
| 176 | + tailscaleServePort: 443, | |
| 177 | + port: 0, | |
| 178 | + host: undefined, | |
| 179 | + desktopBootstrapToken: undefined, | |
| 180 | + staticDir: undefined, | |
| 181 | + devUrl, | |
| 182 | + noBrowser: false, | |
| 183 | + startupPresentation: "browser", | |
| 184 | + }); | |
| 185 | +}); | |
| 151 | 186 | |
| 152 | − return { | |
| 153 | − logLevel: "Error", | |
| 154 | − traceMinLevel: "Info", | |
| 155 | − traceTimingEnabled: true, | |
| 156 | − traceBatchWindowMs: 200, | |
| 157 | − traceMaxBytes: 10 * 1024 * 1024, | |
| 158 | − traceMaxFiles: 10, | |
| 159 | − otlpTracesUrl: undefined, | |
| 160 | − otlpMetricsUrl: undefined, | |
| 161 | − otlpExportIntervalMs: 10_000, | |
| 162 | − otlpServiceName: "t3-server", | |
| 163 | − cwd, | |
| 164 | − baseDir, | |
| 165 | − ...derivedPaths, | |
| 166 | − mode: "web", | |
| 167 | − autoBootstrapProjectFromCwd: false, | |
| 168 | − logWebSocketEvents: false, | |
| 169 | − tailscaleServeEnabled: false, | |
| 170 | − tailscaleServePort: 443, | |
| 171 | − port: 0, | |
| 172 | − host: undefined, | |
| 173 | − desktopBootstrapToken: undefined, | |
| 174 | − staticDir: undefined, | |
| 175 | − devUrl, | |
| 176 | − noBrowser: false, | |
| 177 | − startupPresentation: "browser", | |
| 178 | − } satisfies ServerConfigShape; | |
| 179 | − }), | |
| 180 | − ); | |
| 181 | −} | |
| 187 | +export const layerTest = (cwd: string, baseDirOrPrefix: string | { readonly prefix: string }) => | |
| 188 | + Layer.effect(ServerConfig, makeTest(cwd, baseDirOrPrefix)); | |
| 182 | 189 | |
| 183 | 190 | export const resolveStaticDir = Effect.fn(function* () { |
| 184 | 191 | const { join, resolve } = yield* Path.Path; |
| @@ -8,15 +8,15 @@ import * as FileSystem from "effect/FileSystem"; · 7 unmodified lines | ||
| 8 | 8 | import * as Layer from "effect/Layer"; |
| 9 | 9 | import * as PlatformError from "effect/PlatformError"; |
| 10 | 10 | |
| 11 | −import { deriveServerPaths, ServerConfig, type ServerConfigShape } from "../../config.ts"; | |
| 12 | −import { ServerEnvironment } from "../Services/ServerEnvironment.ts"; | |
| 11 | +import * as ServerConfig from "../../config.ts"; | |
| 12 | +import * as ServerEnvironment from "../Services/ServerEnvironment.ts"; | |
| 13 | 13 | import { ServerEnvironmentLive } from "./ServerEnvironment.ts"; |
| 14 | 14 | |
| 15 | 15 | const makeServerEnvironmentLayer = (baseDir: string) => |
| 16 | 16 | ServerEnvironmentLive.pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), baseDir))); |
| 17 | 17 | |
| 18 | 18 | const makeServerConfig = Effect.fn(function* (baseDir: string) { |
| 19 | − const derivedPaths = yield* deriveServerPaths(baseDir, undefined); | |
| 19 | + const derivedPaths = yield* ServerConfig.deriveServerPaths(baseDir, undefined); | |
| 20 | 20 | |
| 21 | 21 | return { |
| 22 | 22 | ...derivedPaths, |
| @@ -44,7 +44,7 @@ const makeServerConfig = Effect.fn(function* (baseDir: string) { · 21 unmodified lines | ||
| 44 | 44 | devUrl: undefined, |
| 45 | 45 | noBrowser: false, |
| 46 | 46 | startupPresentation: "browser", |
| 47 | − } satisfies ServerConfigShape; | |
| 47 | + } satisfies ServerConfig.ServerConfig["Service"]; | |
| 48 | 48 | }); |
| 49 | 49 | |
| 50 | 50 | it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { |
| @@ -56,11 +56,11 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { · 5 unmodified lines | ||
| 56 | 56 | }); |
| 57 | 57 | |
| 58 | 58 | const first = yield* Effect.gen(function* () { |
| 59 | − const serverEnvironment = yield* ServerEnvironment; | |
| 59 | + const serverEnvironment = yield* ServerEnvironment.ServerEnvironment; | |
| 60 | 60 | return yield* serverEnvironment.getDescriptor; |
| 61 | 61 | }).pipe(Effect.provide(makeServerEnvironmentLayer(baseDir))); |
| 62 | 62 | const second = yield* Effect.gen(function* () { |
| 63 | − const serverEnvironment = yield* ServerEnvironment; | |
| 63 | + const serverEnvironment = yield* ServerEnvironment.ServerEnvironment; | |
| 64 | 64 | return yield* serverEnvironment.getDescriptor; |
| 65 | 65 | }).pipe(Effect.provide(makeServerEnvironmentLayer(baseDir))); |
| 66 | 66 | |
| @@ -109,14 +109,12 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { · 42 unmodified lines | ||
| 109 | 109 | }); |
| 110 | 110 | |
| 111 | 111 | const exit = yield* Effect.gen(function* () { |
| 112 | − const serverEnvironment = yield* ServerEnvironment; | |
| 112 | + const serverEnvironment = yield* ServerEnvironment.ServerEnvironment; | |
| 113 | 113 | return yield* serverEnvironment.getDescriptor; |
| 114 | 114 | }).pipe( |
| 115 | 115 | Effect.provide( |
| 116 | 116 | ServerEnvironmentLive.pipe( |
| 117 | − Layer.provide( | |
| 118 | − Layer.merge(Layer.succeed(ServerConfig, serverConfig), failingFileSystemLayer), | |
| 119 | − ), | |
| 117 | + Layer.provide(Layer.merge(ServerConfig.layer(serverConfig), failingFileSystemLayer)), | |
| 120 | 118 | ), |
| 121 | 119 | ), |
| 122 | 120 | Effect.exit, |
| @@ -9,28 +9,21 @@ import * as Layer from "effect/Layer"; · 8 unmodified lines | ||
| 9 | 9 | import * as Logger from "effect/Logger"; |
| 10 | 10 | import * as Path from "effect/Path"; |
| 11 | 11 | import * as Schema from "effect/Schema"; |
| 12 | −import { ServerConfig } from "./config.ts"; | |
| 13 | − | |
| 14 | −import { | |
| 15 | − DEFAULT_KEYBINDINGS, | |
| 16 | − Keybindings, | |
| 17 | − KeybindingsLive, | |
| 18 | − ResolvedKeybindingFromConfig, | |
| 19 | − compileResolvedKeybindingRule, | |
| 20 | − compileResolvedKeybindingsConfig, | |
| 21 | − parseKeybindingShortcut, | |
| 22 | −} from "./keybindings.ts"; | |
| 12 | +import * as ServerConfig from "./config.ts"; | |
| 13 | +import * as Keybindings from "./keybindings.ts"; | |
| 23 | 14 | import { KeybindingsConfigError } from "@t3tools/contracts"; |
| 24 | 15 | |
| 25 | 16 | const KeybindingsConfigJson = Schema.fromJsonString(KeybindingsConfig); |
| 26 | 17 | const encodeKeybindingsConfigJson = Schema.encodeEffect(KeybindingsConfigJson); |
| 27 | 18 | const decodeKeybindingsConfigJson = Schema.decodeUnknownEffect(KeybindingsConfigJson); |
| 28 | −const encodeResolvedKeybindingFromConfig = Schema.encodeEffect(ResolvedKeybindingFromConfig); | |
| 19 | +const encodeResolvedKeybindingFromConfig = Schema.encodeEffect( | |
| 20 | + Keybindings.ResolvedKeybindingFromConfig, | |
| 21 | +); | |
| 29 | 22 | const decodeResolvedKeybindingFromConfigExit = Schema.decodeUnknownExit( |
| 30 | − ResolvedKeybindingFromConfig, | |
| 23 | + Keybindings.ResolvedKeybindingFromConfig, | |
| 31 | 24 | ); |
| 32 | 25 | const makeKeybindingsLayer = () => { |
| 33 | − return KeybindingsLive.pipe( | |
| 26 | + return Keybindings.layer.pipe( | |
| 34 | 27 | Layer.provideMerge( |
| 35 | 28 | Layer.fresh( |
| 36 | 29 | ServerConfig.layerTest(process.cwd(), { |
| @@ -66,7 +59,7 @@ const readKeybindingsConfig = (configPath: string) => · 29 unmodified lines | ||
| 66 | 59 | it.layer(NodeServices.layer)("keybindings", (it) => { |
| 67 | 60 | it.effect("parses shortcuts including plus key", () => |
| 68 | 61 | Effect.sync(() => { |
| 69 | − assert.deepEqual(parseKeybindingShortcut("mod+j"), { | |
| 62 | + assert.deepEqual(Keybindings.parseKeybindingShortcut("mod+j"), { | |
| 70 | 63 | key: "j", |
| 71 | 64 | metaKey: false, |
| 72 | 65 | ctrlKey: false, |
| @@ -74,7 +67,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 1 unmodified line | ||
| 74 | 67 | altKey: false, |
| 75 | 68 | modKey: true, |
| 76 | 69 | }); |
| 77 | − assert.deepEqual(parseKeybindingShortcut("mod++"), { | |
| 70 | + assert.deepEqual(Keybindings.parseKeybindingShortcut("mod++"), { | |
| 78 | 71 | key: "+", |
| 79 | 72 | metaKey: false, |
| 80 | 73 | ctrlKey: false, |
| @@ -87,7 +80,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 6 unmodified lines | ||
| 87 | 80 | |
| 88 | 81 | it.effect("compiles valid rule with parsed when AST", () => |
| 89 | 82 | Effect.sync(() => { |
| 90 | − const compiled = compileResolvedKeybindingRule({ | |
| 83 | + const compiled = Keybindings.compileResolvedKeybindingRule({ | |
| 91 | 84 | key: "mod+d", |
| 92 | 85 | command: "terminal.split", |
| 93 | 86 | when: "terminalOpen && !terminalFocus", |
| @@ -137,14 +130,14 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 43 unmodified lines | ||
| 137 | 130 | it.effect("rejects invalid rules", () => |
| 138 | 131 | Effect.sync(() => { |
| 139 | 132 | assert.isNull( |
| 140 | − compileResolvedKeybindingRule({ | |
| 133 | + Keybindings.compileResolvedKeybindingRule({ | |
| 141 | 134 | key: "mod+shift+d+o", |
| 142 | 135 | command: "terminal.new", |
| 143 | 136 | }), |
| 144 | 137 | ); |
| 145 | 138 | |
| 146 | 139 | assert.isNull( |
| 147 | − compileResolvedKeybindingRule({ | |
| 140 | + Keybindings.compileResolvedKeybindingRule({ | |
| 148 | 141 | key: "mod+d", |
| 149 | 142 | command: "terminal.split", |
| 150 | 143 | when: "terminalFocus && (", |
| @@ -152,7 +145,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 1 unmodified line | ||
| 152 | 145 | ); |
| 153 | 146 | |
| 154 | 147 | assert.isNull( |
| 155 | − compileResolvedKeybindingRule({ | |
| 148 | + Keybindings.compileResolvedKeybindingRule({ | |
| 156 | 149 | key: "mod+d", |
| 157 | 150 | command: "terminal.split", |
| 158 | 151 | when: `${"!".repeat(300)}terminalFocus`, |
| @@ -181,23 +174,23 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 22 unmodified lines | ||
| 181 | 174 | it.effect("bootstraps default keybindings when config file is missing", () => |
| 182 | 175 | Effect.gen(function* () { |
| 183 | 176 | const fs = yield* FileSystem.FileSystem; |
| 184 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 177 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 185 | 178 | assert.isFalse(yield* fs.exists(keybindingsConfigPath)); |
| 186 | 179 | |
| 187 | 180 | yield* Effect.gen(function* () { |
| 188 | − const keybindings = yield* Keybindings; | |
| 181 | + const keybindings = yield* Keybindings.Keybindings; | |
| 189 | 182 | yield* keybindings.syncDefaultKeybindingsOnStartup; |
| 190 | 183 | }); |
| 191 | 184 | |
| 192 | 185 | const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); |
| 193 | − assert.deepEqual(persisted, DEFAULT_KEYBINDINGS); | |
| 186 | + assert.deepEqual(persisted, Keybindings.DEFAULT_KEYBINDINGS); | |
| 194 | 187 | }).pipe(Effect.provide(makeKeybindingsLayer())), |
| 195 | 188 | ); |
| 196 | 189 | |
| 197 | 190 | it.effect("ships configurable thread navigation defaults", () => |
| 198 | 191 | Effect.sync(() => { |
| 199 | 192 | const defaultsByCommand = new Map( |
| 200 | − DEFAULT_KEYBINDINGS.map((binding) => [binding.command, binding.key] as const), | |
| 193 | + Keybindings.DEFAULT_KEYBINDINGS.map((binding) => [binding.command, binding.key] as const), | |
| 201 | 194 | ); |
| 202 | 195 | |
| 203 | 196 | assert.equal(defaultsByCommand.get("thread.previous"), "mod+shift+["); |
| @@ -215,17 +208,17 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 11 unmodified lines | ||
| 215 | 208 | it.effect("uses defaults in runtime when config is malformed without overriding file", () => |
| 216 | 209 | Effect.gen(function* () { |
| 217 | 210 | const fs = yield* FileSystem.FileSystem; |
| 218 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 211 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 219 | 212 | yield* fs.writeFileString(keybindingsConfigPath, "{ not-json"); |
| 220 | 213 | |
| 221 | 214 | const configState = yield* Effect.gen(function* () { |
| 222 | − const keybindings = yield* Keybindings; | |
| 215 | + const keybindings = yield* Keybindings.Keybindings; | |
| 223 | 216 | return yield* keybindings.loadConfigState; |
| 224 | 217 | }); |
| 225 | 218 | |
| 226 | 219 | assert.deepEqual( |
| 227 | 220 | configState.keybindings, |
| 228 | − compileResolvedKeybindingsConfig(DEFAULT_KEYBINDINGS), | |
| 221 | + Keybindings.compileResolvedKeybindingsConfig(Keybindings.DEFAULT_KEYBINDINGS), | |
| 229 | 222 | ); |
| 230 | 223 | assert.deepEqual(configState.issues, [ |
| 231 | 224 | { |
| @@ -240,7 +233,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 8 unmodified lines | ||
| 240 | 233 | it.effect("ignores invalid entries in runtime and reports them as issues", () => |
| 241 | 234 | Effect.gen(function* () { |
| 242 | 235 | const fs = yield* FileSystem.FileSystem; |
| 243 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 236 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 244 | 237 | yield* fs.writeFileString( |
| 245 | 238 | keybindingsConfigPath, |
| 246 | 239 | // @effect-diagnostics-next-line preferSchemaOverJson:off |
| @@ -252,7 +245,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 5 unmodified lines | ||
| 252 | 245 | ); |
| 253 | 246 | |
| 254 | 247 | const configState = yield* Effect.gen(function* () { |
| 255 | − const keybindings = yield* Keybindings; | |
| 248 | + const keybindings = yield* Keybindings.Keybindings; | |
| 256 | 249 | return yield* keybindings.loadConfigState; |
| 257 | 250 | }); |
| 258 | 251 | |
| @@ -279,14 +272,14 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 20 unmodified lines | ||
| 279 | 272 | "upserts missing default keybindings on startup without overriding existing command rules", |
| 280 | 273 | () => |
| 281 | 274 | Effect.gen(function* () { |
| 282 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 275 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 283 | 276 | yield* writeKeybindingsConfig(keybindingsConfigPath, [ |
| 284 | 277 | { key: "mod+shift+t", command: "terminal.toggle" }, |
| 285 | 278 | { key: "mod+shift+r", command: "script.run-tests.run" }, |
| 286 | 279 | ]); |
| 287 | 280 | |
| 288 | 281 | yield* Effect.gen(function* () { |
| 289 | − const keybindings = yield* Keybindings; | |
| 282 | + const keybindings = yield* Keybindings.Keybindings; | |
| 290 | 283 | yield* keybindings.syncDefaultKeybindingsOnStartup; |
| 291 | 284 | }); |
| 292 | 285 | |
| @@ -300,7 +293,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 7 unmodified lines | ||
| 300 | 293 | persisted.some((entry) => entry.command === "terminal.toggle" && entry.key === "mod+j"), |
| 301 | 294 | ); |
| 302 | 295 | |
| 303 | − for (const defaultRule of DEFAULT_KEYBINDINGS) { | |
| 296 | + for (const defaultRule of Keybindings.DEFAULT_KEYBINDINGS) { | |
| 304 | 297 | assert.isTrue(byCommand.has(defaultRule.command), `expected ${defaultRule.command}`); |
| 305 | 298 | } |
| 306 | 299 | assert.isTrue(byCommand.has("script.run-tests.run")); |
| @@ -314,13 +307,13 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 7 unmodified lines | ||
| 314 | 307 | }); |
| 315 | 308 | |
| 316 | 309 | return Effect.gen(function* () { |
| 317 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 310 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 318 | 311 | yield* writeKeybindingsConfig(keybindingsConfigPath, [ |
| 319 | 312 | { key: "mod+j", command: "script.custom-action.run" }, |
| 320 | 313 | ]); |
| 321 | 314 | |
| 322 | 315 | yield* Effect.gen(function* () { |
| 323 | − const keybindings = yield* Keybindings; | |
| 316 | + const keybindings = yield* Keybindings.Keybindings; | |
| 324 | 317 | yield* keybindings.syncDefaultKeybindingsOnStartup; |
| 325 | 318 | }); |
| 326 | 319 | |
| @@ -345,13 +338,13 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 18 unmodified lines | ||
| 345 | 338 | |
| 346 | 339 | it.effect("upserts custom keybindings to configured path", () => |
| 347 | 340 | Effect.gen(function* () { |
| 348 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 341 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 349 | 342 | yield* writeKeybindingsConfig(keybindingsConfigPath, [ |
| 350 | 343 | { key: "mod+j", command: "terminal.toggle" }, |
| 351 | 344 | ]); |
| 352 | 345 | |
| 353 | 346 | const resolved = yield* Effect.gen(function* () { |
| 354 | − const keybindings = yield* Keybindings; | |
| 347 | + const keybindings = yield* Keybindings.Keybindings; | |
| 355 | 348 | return yield* keybindings.upsertKeybindingRule({ |
| 356 | 349 | key: "mod+shift+r", |
| 357 | 350 | command: "script.run-tests.run", |
| @@ -371,12 +364,12 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 13 unmodified lines | ||
| 371 | 364 | |
| 372 | 365 | it.effect("appends additional custom keybindings for the same command", () => |
| 373 | 366 | Effect.gen(function* () { |
| 374 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 367 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 375 | 368 | yield* writeKeybindingsConfig(keybindingsConfigPath, [ |
| 376 | 369 | { key: "mod+r", command: "script.run-tests.run" }, |
| 377 | 370 | ]); |
| 378 | 371 | yield* Effect.gen(function* () { |
| 379 | − const keybindings = yield* Keybindings; | |
| 372 | + const keybindings = yield* Keybindings.Keybindings; | |
| 380 | 373 | return yield* keybindings.upsertKeybindingRule({ |
| 381 | 374 | key: "mod+shift+r", |
| 382 | 375 | command: "script.run-tests.run", |
| @@ -394,13 +387,13 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 11 unmodified lines | ||
| 394 | 387 | |
| 395 | 388 | it.effect("replaces only the targeted custom keybinding", () => |
| 396 | 389 | Effect.gen(function* () { |
| 397 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 390 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 398 | 391 | yield* writeKeybindingsConfig(keybindingsConfigPath, [ |
| 399 | 392 | { key: "mod+r", command: "script.run-tests.run" }, |
| 400 | 393 | { key: "mod+shift+r", command: "script.run-tests.run" }, |
| 401 | 394 | ]); |
| 402 | 395 | yield* Effect.gen(function* () { |
| 403 | − const keybindings = yield* Keybindings; | |
| 396 | + const keybindings = yield* Keybindings.Keybindings; | |
| 404 | 397 | return yield* keybindings.upsertKeybindingRule({ |
| 405 | 398 | key: "mod+alt+r", |
| 406 | 399 | command: "script.run-tests.run", |
| @@ -419,13 +412,13 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 12 unmodified lines | ||
| 419 | 412 | |
| 420 | 413 | it.effect("removes only the targeted custom keybinding", () => |
| 421 | 414 | Effect.gen(function* () { |
| 422 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 415 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 423 | 416 | yield* writeKeybindingsConfig(keybindingsConfigPath, [ |
| 424 | 417 | { key: "mod+r", command: "script.run-tests.run" }, |
| 425 | 418 | { key: "mod+shift+r", command: "script.run-tests.run" }, |
| 426 | 419 | ]); |
| 427 | 420 | yield* Effect.gen(function* () { |
| 428 | − const keybindings = yield* Keybindings; | |
| 421 | + const keybindings = yield* Keybindings.Keybindings; | |
| 429 | 422 | return yield* keybindings.removeKeybindingRule({ |
| 430 | 423 | key: "mod+r", |
| 431 | 424 | command: "script.run-tests.run", |
| @@ -441,11 +434,11 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 9 unmodified lines | ||
| 441 | 434 | it.effect("refuses to overwrite malformed keybindings config", () => |
| 442 | 435 | Effect.gen(function* () { |
| 443 | 436 | const fs = yield* FileSystem.FileSystem; |
| 444 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 437 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 445 | 438 | yield* fs.writeFileString(keybindingsConfigPath, "{ not-json"); |
| 446 | 439 | |
| 447 | 440 | const result = yield* Effect.gen(function* () { |
| 448 | − const keybindings = yield* Keybindings; | |
| 441 | + const keybindings = yield* Keybindings.Keybindings; | |
| 449 | 442 | return yield* keybindings.upsertKeybindingRule({ |
| 450 | 443 | key: "mod+shift+r", |
| 451 | 444 | command: "script.run-tests.run", |
| @@ -461,14 +454,14 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 9 unmodified lines | ||
| 461 | 454 | it.effect("reports non-array config parse errors without duplicate prefix", () => |
| 462 | 455 | Effect.gen(function* () { |
| 463 | 456 | const fs = yield* FileSystem.FileSystem; |
| 464 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 457 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 465 | 458 | yield* fs.writeFileString( |
| 466 | 459 | keybindingsConfigPath, |
| 467 | 460 | '{"key":"mod+j","command":"terminal.toggle"}', |
| 468 | 461 | ); |
| 469 | 462 | |
| 470 | 463 | const firstResult = yield* Effect.gen(function* () { |
| 471 | − const keybindings = yield* Keybindings; | |
| 464 | + const keybindings = yield* Keybindings.Keybindings; | |
| 472 | 465 | return yield* keybindings.upsertKeybindingRule({ |
| 473 | 466 | key: "mod+shift+r", |
| 474 | 467 | command: "script.run-tests.run", |
| @@ -477,7 +470,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 2 unmodified lines | ||
| 477 | 470 | assertFailure(firstResult, "expected JSON array"); |
| 478 | 471 | |
| 479 | 472 | const secondResult = yield* Effect.gen(function* () { |
| 480 | − const keybindings = yield* Keybindings; | |
| 473 | + const keybindings = yield* Keybindings.Keybindings; | |
| 481 | 474 | return yield* keybindings.upsertKeybindingRule({ |
| 482 | 475 | key: "mod+shift+r", |
| 483 | 476 | command: "script.run-tests.run", |
| @@ -490,7 +483,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 6 unmodified lines | ||
| 490 | 483 | it.effect("fails when config directory is not writable", () => |
| 491 | 484 | Effect.gen(function* () { |
| 492 | 485 | const fs = yield* FileSystem.FileSystem; |
| 493 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 486 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 494 | 487 | const { dirname } = yield* Path.Path; |
| 495 | 488 | yield* writeKeybindingsConfig(keybindingsConfigPath, [ |
| 496 | 489 | { key: "mod+j", command: "terminal.toggle" }, |
| @@ -498,7 +491,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 1 unmodified line | ||
| 498 | 491 | yield* fs.chmod(dirname(keybindingsConfigPath), 0o500); |
| 499 | 492 | |
| 500 | 493 | const result = yield* Effect.gen(function* () { |
| 501 | − const keybindings = yield* Keybindings; | |
| 494 | + const keybindings = yield* Keybindings.Keybindings; | |
| 502 | 495 | return yield* keybindings.upsertKeybindingRule({ |
| 503 | 496 | key: "mod+shift+r", |
| 504 | 497 | command: "script.run-tests.run", |
| @@ -516,13 +509,13 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 11 unmodified lines | ||
| 516 | 509 | |
| 517 | 510 | it.effect("caches loaded resolved config across repeated reads", () => |
| 518 | 511 | Effect.gen(function* () { |
| 519 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 512 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 520 | 513 | yield* writeKeybindingsConfig(keybindingsConfigPath, [ |
| 521 | 514 | { key: "mod+j", command: "terminal.toggle" }, |
| 522 | 515 | ]); |
| 523 | 516 | |
| 524 | 517 | const [first, second] = yield* Effect.gen(function* () { |
| 525 | − const keybindings = yield* Keybindings; | |
| 518 | + const keybindings = yield* Keybindings.Keybindings; | |
| 526 | 519 | const firstLoad = (yield* keybindings.loadConfigState).keybindings; |
| 527 | 520 | const secondLoad = (yield* keybindings.loadConfigState).keybindings; |
| 528 | 521 | return [firstLoad, secondLoad] as const; |
| @@ -535,13 +528,13 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 6 unmodified lines | ||
| 535 | 528 | |
| 536 | 529 | it.effect("updates cached resolved config after upsert", () => |
| 537 | 530 | Effect.gen(function* () { |
| 538 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 531 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 539 | 532 | yield* writeKeybindingsConfig(keybindingsConfigPath, [ |
| 540 | 533 | { key: "mod+j", command: "terminal.toggle" }, |
| 541 | 534 | ]); |
| 542 | 535 | |
| 543 | 536 | const loadedAfterUpsert = yield* Effect.gen(function* () { |
| 544 | − const keybindings = yield* Keybindings; | |
| 537 | + const keybindings = yield* Keybindings.Keybindings; | |
| 545 | 538 | yield* keybindings.loadConfigState; |
| 546 | 539 | yield* keybindings.upsertKeybindingRule({ |
| 547 | 540 | key: "mod+shift+r", |
| @@ -557,7 +550,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 9 unmodified lines | ||
| 557 | 550 | |
| 558 | 551 | it.effect("serializes concurrent upserts to avoid lost updates", () => |
| 559 | 552 | Effect.gen(function* () { |
| 560 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 553 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 561 | 554 | yield* writeKeybindingsConfig(keybindingsConfigPath, []); |
| 562 | 555 | |
| 563 | 556 | const commands = Array.from( |
| @@ -565,7 +558,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { · 1 unmodified line | ||
| 565 | 558 | (_, index): KeybindingCommand => `script.concurrent-${index}.run`, |
| 566 | 559 | ); |
| 567 | 560 | yield* Effect.gen(function* () { |
| 568 | − const keybindings = yield* Keybindings; | |
| 561 | + const keybindings = yield* Keybindings.Keybindings; | |
| 569 | 562 | yield* Effect.all( |
| 570 | 563 | commands.map((command, index) => |
| 571 | 564 | keybindings.upsertKeybindingRule({ |
| @@ -41,7 +41,7 @@ import * as Context from "effect/Context"; · 40 unmodified lines | ||
| 41 | 41 | import * as Scope from "effect/Scope"; |
| 42 | 42 | import * as Stream from "effect/Stream"; |
| 43 | 43 | import * as Semaphore from "effect/Semaphore"; |
| 44 | −import { ServerConfig } from "./config.ts"; | |
| 44 | +import * as ServerConfig from "./config.ts"; | |
| 45 | 45 | import { writeFileStringAtomically } from "./atomicWrite.ts"; |
| 46 | 46 | import { fromJsonStringPretty, fromLenientJson } from "@t3tools/shared/schemaJson"; |
| 47 | 47 | import { |
| @@ -225,74 +225,70 @@ function mergeWithDefaultKeybindings(custom: ResolvedKeybindingsConfig): Resolve · 177 unmodified lines | ||
| 225 | 225 | return merged.slice(-MAX_KEYBINDINGS_COUNT); |
| 226 | 226 | } |
| 227 | 227 | |
| 228 | −/** | |
| 229 | − * KeybindingsShape - Service API for keybinding configuration operations. | |
| 230 | − */ | |
| 231 | −export interface KeybindingsShape { | |
| 232 | − /** | |
| 233 | − * Start the keybindings runtime and attach file watching. | |
| 234 | − * | |
| 235 | − * Safe to call multiple times. The first successful call establishes the | |
| 236 | − * runtime; later calls await the same startup. | |
| 237 | − */ | |
| 238 | − readonly start: Effect.Effect<void, KeybindingsConfigError>; | |
| 239 | − | |
| 240 | − /** | |
| 241 | − * Await keybindings runtime readiness. | |
| 242 | − * | |
| 243 | − * Readiness means the config directory exists, the watcher is attached, the | |
| 244 | − * startup sync has completed, and the current snapshot has been loaded. | |
| 245 | − */ | |
| 246 | − readonly ready: Effect.Effect<void, KeybindingsConfigError>; | |
| 247 | − | |
| 248 | − /** | |
| 249 | − * Ensure the on-disk keybindings file exists and includes all default | |
| 250 | − * commands so newly-added defaults are backfilled on startup. | |
| 251 | − */ | |
| 252 | − readonly syncDefaultKeybindingsOnStartup: Effect.Effect<void, KeybindingsConfigError>; | |
| 253 | − | |
| 254 | − /** | |
| 255 | − * Load runtime keybindings state along with non-fatal configuration issues. | |
| 256 | − */ | |
| 257 | − readonly loadConfigState: Effect.Effect<KeybindingsConfigState, KeybindingsConfigError>; | |
| 258 | − | |
| 259 | − /** | |
| 260 | − * Read the latest keybindings snapshot from cache/disk. | |
| 261 | − */ | |
| 262 | − readonly getSnapshot: Effect.Effect<KeybindingsConfigState, KeybindingsConfigError>; | |
| 263 | − | |
| 264 | − /** | |
| 265 | − * Stream of keybindings config change events. | |
| 266 | − */ | |
| 267 | − readonly streamChanges: Stream.Stream<KeybindingsChangeEvent>; | |
| 268 | − | |
| 269 | − /** | |
| 270 | − * Upsert a keybinding rule and persist the resulting configuration. | |
| 271 | − * | |
| 272 | − * Writes config atomically and enforces the max rule count by truncating | |
| 273 | − * oldest entries when needed. | |
| 274 | − */ | |
| 275 | − readonly upsertKeybindingRule: ( | |
| 276 | − input: ServerUpsertKeybindingInput, | |
| 277 | − ) => Effect.Effect<ResolvedKeybindingsConfig, KeybindingsConfigError>; | |
| 278 | − | |
| 279 | − /** | |
| 280 | − * Remove a single persisted keybinding rule by exact key/command/when match. | |
| 281 | − */ | |
| 282 | − readonly removeKeybindingRule: ( | |
| 283 | − input: ServerRemoveKeybindingInput, | |
| 284 | − ) => Effect.Effect<ResolvedKeybindingsConfig, KeybindingsConfigError>; | |
| 285 | −} | |
| 286 | − | |
| 287 | 228 | /** |
| 288 | 229 | * Keybindings - Service tag for keybinding configuration operations. |
| 289 | 230 | */ |
| 290 | −export class Keybindings extends Context.Service<Keybindings, KeybindingsShape>()( | |
| 291 | − "t3/keybindings", | |
| 292 | −) {} | |
| 231 | +export class Keybindings extends Context.Service< | |
| 232 | + Keybindings, | |
| 233 | + { | |
| 234 | + /** | |
| 235 | + * Start the keybindings runtime and attach file watching. | |
| 236 | + * | |
| 237 | + * Safe to call multiple times. The first successful call establishes the | |
| 238 | + * runtime; later calls await the same startup. | |
| 239 | + */ | |
| 240 | + readonly start: Effect.Effect<void, KeybindingsConfigError>; | |
| 241 | + | |
| 242 | + /** | |
| 243 | + * Await keybindings runtime readiness. | |
| 244 | + * | |
| 245 | + * Readiness means the config directory exists, the watcher is attached, the | |
| 246 | + * startup sync has completed, and the current snapshot has been loaded. | |
| 247 | + */ | |
| 248 | + readonly ready: Effect.Effect<void, KeybindingsConfigError>; | |
| 249 | + | |
| 250 | + /** | |
| 251 | + * Ensure the on-disk keybindings file exists and includes all default | |
| 252 | + * commands so newly-added defaults are backfilled on startup. | |
| 253 | + */ | |
| 254 | + readonly syncDefaultKeybindingsOnStartup: Effect.Effect<void, KeybindingsConfigError>; | |
| 255 | + | |
| 256 | + /** | |
| 257 | + * Load runtime keybindings state along with non-fatal configuration issues. | |
| 258 | + */ | |
| 259 | + readonly loadConfigState: Effect.Effect<KeybindingsConfigState, KeybindingsConfigError>; | |
| 260 | + | |
| 261 | + /** | |
| 262 | + * Read the latest keybindings snapshot from cache/disk. | |
| 263 | + */ | |
| 264 | + readonly getSnapshot: Effect.Effect<KeybindingsConfigState, KeybindingsConfigError>; | |
| 265 | + | |
| 266 | + /** | |
| 267 | + * Stream of keybindings config change events. | |
| 268 | + */ | |
| 269 | + readonly streamChanges: Stream.Stream<KeybindingsChangeEvent>; | |
| 270 | + | |
| 271 | + /** | |
| 272 | + * Upsert a keybinding rule and persist the resulting configuration. | |
| 273 | + * | |
| 274 | + * Writes config atomically and enforces the max rule count by truncating | |
| 275 | + * oldest entries when needed. | |
| 276 | + */ | |
| 277 | + readonly upsertKeybindingRule: ( | |
| 278 | + input: ServerUpsertKeybindingInput, | |
| 279 | + ) => Effect.Effect<ResolvedKeybindingsConfig, KeybindingsConfigError>; | |
| 280 | + | |
| 281 | + /** | |
| 282 | + * Remove a single persisted keybinding rule by exact key/command/when match. | |
| 283 | + */ | |
| 284 | + readonly removeKeybindingRule: ( | |
| 285 | + input: ServerRemoveKeybindingInput, | |
| 286 | + ) => Effect.Effect<ResolvedKeybindingsConfig, KeybindingsConfigError>; | |
| 287 | + } | |
| 288 | +>()("t3/keybindings") {} | |
| 293 | 289 | |
| 294 | −const makeKeybindings = Effect.gen(function* () { | |
| 295 | − const { keybindingsConfigPath } = yield* ServerConfig; | |
| 290 | +const make = Effect.gen(function* () { | |
| 291 | + const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | |
| 296 | 292 | const fs = yield* FileSystem.FileSystem; |
| 297 | 293 | const path = yield* Path.Path; |
| 298 | 294 | const upsertSemaphore = yield* Semaphore.make(1); |
| @@ -700,7 +696,7 @@ const makeKeybindings = Effect.gen(function* () { · 401 unmodified lines | ||
| 700 | 696 | return nextResolved; |
| 701 | 697 | }), |
| 702 | 698 | ), |
| 703 | − } satisfies KeybindingsShape; | |
| 699 | + } satisfies Keybindings["Service"]; | |
| 704 | 700 | }); |
| 705 | 701 | |
| 706 | −export const KeybindingsLive = Layer.effect(Keybindings, makeKeybindings); | |
| 702 | +export const layer = Layer.effect(Keybindings, make); | |
| @@ -107,7 +107,15 @@ it.effect("registers annotated tools and preserves authenticated request context · 106 unmodified lines | ||
| 107 | 107 | Effect.gen(function* () { |
| 108 | 108 | const server = yield* McpServer.McpServer; |
| 109 | 109 | const broker = yield* PreviewAutomationBroker.PreviewAutomationBroker; |
| 110 | − const requests = yield* broker.connect("mcp-test-client"); | |
| 110 | + const requests = yield* broker.connect({ | |
| 111 | + clientId: "mcp-test-client", | |
| 112 | + environmentId, | |
| 113 | + threadId, | |
| 114 | + tabId, | |
| 115 | + visible: true, | |
| 116 | + supportsAutomation: true, | |
| 117 | + focusedAt: "2026-06-11T00:00:00.000Z", | |
| 118 | + }); | |
| 111 | 119 | yield* Stream.runForEach(requests, (request) => |
| 112 | 120 | broker.respond({ |
| 113 | 121 | requestId: request.requestId, |
| @@ -2,10 +2,13 @@ import { expect, it } from "@effect/vitest"; · 1 unmodified line | ||
| 2 | 2 | import { |
| 3 | 3 | EnvironmentId, |
| 4 | 4 | PreviewAutomationNoFocusedOwnerError, |
| 5 | + PreviewAutomationUnavailableError, | |
| 5 | 6 | ProviderInstanceId, |
| 6 | 7 | ThreadId, |
| 8 | + type PreviewAutomationOwner, | |
| 7 | 9 | } from "@t3tools/contracts"; |
| 8 | 10 | import * as Effect from "effect/Effect"; |
| 11 | +import * as Fiber from "effect/Fiber"; | |
| 9 | 12 | import * as Stream from "effect/Stream"; |
| 10 | 13 | |
| 11 | 14 | import * as PreviewAutomationBroker from "./PreviewAutomationBroker.ts"; |
| @@ -20,11 +23,22 @@ const scope = { · 8 unmodified lines | ||
| 20 | 23 | expiresAt: 2, |
| 21 | 24 | }; |
| 22 | 25 | |
| 23 | −it.effect("routes a request to the focused owner and correlates its response", () => | |
| 26 | +const makeOwner = (overrides: Partial<PreviewAutomationOwner> = {}): PreviewAutomationOwner => ({ | |
| 27 | + clientId: "client-1", | |
| 28 | + environmentId: scope.environmentId, | |
| 29 | + threadId: scope.threadId, | |
| 30 | + tabId: null, | |
| 31 | + visible: false, | |
| 32 | + supportsAutomation: true, | |
| 33 | + focusedAt: "2026-06-11T00:00:00.000Z", | |
| 34 | + ...overrides, | |
| 35 | +}); | |
| 36 | + | |
| 37 | +it.effect("atomically registers a connected owner and correlates its response", () => | |
| 24 | 38 | Effect.scoped( |
| 25 | 39 | Effect.gen(function* () { |
| 26 | 40 | const broker = yield* PreviewAutomationBroker.__testing.make; |
| 27 | − const requests = yield* broker.connect("client-1"); | |
| 41 | + const requests = yield* broker.connect(makeOwner()); | |
| 28 | 42 | yield* Stream.runForEach(requests, (request) => |
| 29 | 43 | broker.respond({ |
| 30 | 44 | requestId: request.requestId, |
| @@ -33,15 +47,6 @@ it.effect("routes a request to the focused owner and correlates its response", ( · 2 unmodified lines | ||
| 33 | 47 | }), |
| 34 | 48 | ).pipe(Effect.forkScoped); |
| 35 | 49 | yield* Effect.yieldNow; |
| 36 | − yield* broker.reportOwner({ | |
| 37 | − clientId: "client-1", | |
| 38 | − environmentId: scope.environmentId, | |
| 39 | − threadId: scope.threadId, | |
| 40 | − tabId: null, | |
| 41 | − visible: false, | |
| 42 | − supportsAutomation: true, | |
| 43 | − focusedAt: "2026-06-11T00:00:00.000Z", | |
| 44 | − }); | |
| 45 | 50 | |
| 46 | 51 | const result = yield* broker.invoke<{ available: boolean }>({ |
| 47 | 52 | scope, |
| @@ -68,22 +73,119 @@ it.effect("routes interactive commands to a hidden durable browser host", () => · 20 unmodified lines | ||
| 68 | 73 | Effect.scoped( |
| 69 | 74 | Effect.gen(function* () { |
| 70 | 75 | const broker = yield* PreviewAutomationBroker.__testing.make; |
| 71 | − const requests = yield* broker.connect("client-hidden"); | |
| 76 | + const requests = yield* broker.connect( | |
| 77 | + makeOwner({ clientId: "client-hidden", tabId: "tab-hidden" }), | |
| 78 | + ); | |
| 79 | + yield* Stream.runForEach(requests, (request) => | |
| 80 | + broker.respond({ requestId: request.requestId, ok: true }), | |
| 81 | + ).pipe(Effect.forkScoped); | |
| 82 | + yield* Effect.yieldNow; | |
| 83 | + | |
| 84 | + yield* broker.invoke<void>({ scope, operation: "click", input: { x: 10, y: 10 } }); | |
| 85 | + }), | |
| 86 | + ), | |
| 87 | +); | |
| 88 | + | |
| 89 | +it.effect("lets the browser host resolve an active tab that has not been reported yet", () => | |
| 90 | + Effect.scoped( | |
| 91 | + Effect.gen(function* () { | |
| 92 | + const broker = yield* PreviewAutomationBroker.__testing.make; | |
| 93 | + const requests = yield* broker.connect(makeOwner({ tabId: null })); | |
| 94 | + let routedTabId: string | undefined; | |
| 95 | + yield* Stream.runForEach(requests, (request) => { | |
| 96 | + routedTabId = request.tabId; | |
| 97 | + return broker.respond({ requestId: request.requestId, ok: true }); | |
| 98 | + }).pipe(Effect.forkScoped); | |
| 99 | + yield* Effect.yieldNow; | |
| 100 | + | |
| 101 | + yield* broker.invoke<void>({ scope, operation: "click", input: { x: 10, y: 10 } }); | |
| 102 | + | |
| 103 | + expect(routedTabId).toBeUndefined(); | |
| 104 | + }), | |
| 105 | + ), | |
| 106 | +); | |
| 107 | + | |
| 108 | +it.effect("preserves current owner metadata when its request stream reconnects", () => | |
| 109 | + Effect.scoped( | |
| 110 | + Effect.gen(function* () { | |
| 111 | + const broker = yield* PreviewAutomationBroker.__testing.make; | |
| 112 | + const firstRequests = yield* broker.connect(makeOwner()); | |
| 113 | + yield* Stream.runDrain(firstRequests).pipe(Effect.forkScoped); | |
| 114 | + yield* broker.reportOwner(makeOwner({ tabId: "tab-current", visible: true })); | |
| 115 | + | |
| 116 | + const reconnectedRequests = yield* broker.connect(makeOwner()); | |
| 117 | + let routedTabId: string | undefined; | |
| 118 | + yield* Stream.runForEach(reconnectedRequests, (request) => { | |
| 119 | + routedTabId = request.tabId; | |
| 120 | + return broker.respond({ requestId: request.requestId, ok: true }); | |
| 121 | + }).pipe(Effect.forkScoped); | |
| 122 | + yield* Effect.yieldNow; | |
| 123 | + | |
| 124 | + yield* broker.invoke<void>({ scope, operation: "click", input: { x: 10, y: 10 } }); | |
| 125 | + | |
| 126 | + expect(routedTabId).toBe("tab-current"); | |
| 127 | + }), | |
| 128 | + ), | |
| 129 | +); | |
| 130 | + | |
| 131 | +it.effect("ignores stale owner cleanup after the client moves to another thread", () => | |
| 132 | + Effect.scoped( | |
| 133 | + Effect.gen(function* () { | |
| 134 | + const broker = yield* PreviewAutomationBroker.__testing.make; | |
| 135 | + const requests = yield* broker.connect(makeOwner()); | |
| 72 | 136 | yield* Stream.runForEach(requests, (request) => |
| 73 | 137 | broker.respond({ requestId: request.requestId, ok: true }), |
| 74 | 138 | ).pipe(Effect.forkScoped); |
| 75 | 139 | yield* Effect.yieldNow; |
| 76 | − yield* broker.reportOwner({ | |
| 77 | − clientId: "client-hidden", | |
| 140 | + | |
| 141 | + yield* broker.clearOwner({ | |
| 142 | + clientId: "client-1", | |
| 78 | 143 | environmentId: scope.environmentId, |
| 79 | − threadId: scope.threadId, | |
| 80 | − tabId: "tab-hidden", | |
| 81 | − visible: false, | |
| 82 | − supportsAutomation: true, | |
| 83 | − focusedAt: "2026-06-11T00:00:00.000Z", | |
| 144 | + threadId: ThreadId.make("thread-stale"), | |
| 84 | 145 | }); |
| 85 | 146 | |
| 86 | − yield* broker.invoke<void>({ scope, operation: "click", input: { x: 10, y: 10 } }); | |
| 147 | + yield* broker.invoke<void>({ scope, operation: "status", input: {} }); | |
| 148 | + }), | |
| 149 | + ), | |
| 150 | +); | |
| 151 | + | |
| 152 | +it.effect("fails requests assigned to a browser stream when that stream reconnects", () => | |
| 153 | + Effect.scoped( | |
| 154 | + Effect.gen(function* () { | |
| 155 | + const broker = yield* PreviewAutomationBroker.__testing.make; | |
| 156 | + const _requests = yield* broker.connect(makeOwner()); | |
| 157 | + const pending = yield* broker | |
| 158 | + .invoke<void>({ scope, operation: "status", input: {} }) | |
| 159 | + .pipe(Effect.flip, Effect.forkScoped); | |
| 160 | + yield* Effect.yieldNow; | |
| 161 | + | |
| 162 | + const _replacementRequests = yield* broker.connect(makeOwner()); | |
| 163 | + | |
| 164 | + const error = yield* Fiber.join(pending); | |
| 165 | + expect(error).toBeInstanceOf(PreviewAutomationUnavailableError); | |
| 166 | + }), | |
| 167 | + ), | |
| 168 | +); | |
| 169 | + | |
| 170 | +it.effect("falls back to an older connected owner when a newer report is not connected", () => | |
| 171 | + Effect.scoped( | |
| 172 | + Effect.gen(function* () { | |
| 173 | + const broker = yield* PreviewAutomationBroker.__testing.make; | |
| 174 | + const requests = yield* broker.connect(makeOwner({ clientId: "client-connected" })); | |
| 175 | + yield* Stream.runForEach(requests, (request) => | |
| 176 | + broker.respond({ requestId: request.requestId, ok: true, result: "connected" }), | |
| 177 | + ).pipe(Effect.forkScoped); | |
| 178 | + yield* Effect.yieldNow; | |
| 179 | + yield* broker.reportOwner( | |
| 180 | + makeOwner({ | |
| 181 | + clientId: "client-report-only", | |
| 182 | + focusedAt: "2026-06-11T00:00:01.000Z", | |
| 183 | + }), | |
| 184 | + ); | |
| 185 | + | |
| 186 | + const result = yield* broker.invoke<string>({ scope, operation: "status", input: {} }); | |
| 187 | + | |
| 188 | + expect(result).toBe("connected"); | |
| 87 | 189 | }), |
| 88 | 190 | ), |
| 89 | 191 | ); |
| @@ -11,6 +11,7 @@ import { · 10 unmodified lines | ||
| 11 | 11 | type PreviewAutomationError, |
| 12 | 12 | type PreviewAutomationOperation, |
| 13 | 13 | type PreviewAutomationOwner, |
| 14 | + type PreviewAutomationOwnerIdentity, | |
| 14 | 15 | type PreviewAutomationRequest, |
| 15 | 16 | type PreviewAutomationResponse, |
| 16 | 17 | type PreviewTabId, |
| @@ -35,11 +36,13 @@ export interface PreviewAutomationInvokeInput { · 18 unmodified lines | ||
| 35 | 36 | } |
| 36 | 37 | |
| 37 | 38 | export interface PreviewAutomationBrokerShape { |
| 38 | − readonly connect: (clientId: string) => Effect.Effect<Stream.Stream<PreviewAutomationRequest>>; | |
| 39 | + readonly connect: ( | |
| 40 | + owner: PreviewAutomationOwner, | |
| 41 | + ) => Effect.Effect<Stream.Stream<PreviewAutomationRequest>>; | |
| 39 | 42 | readonly reportOwner: ( |
| 40 | 43 | owner: PreviewAutomationOwner, |
| 41 | 44 | ) => Effect.Effect<void, PreviewAutomationError>; |
| 42 | − readonly clearOwner: (clientId: string) => Effect.Effect<void>; | |
| 45 | + readonly clearOwner: (owner: PreviewAutomationOwnerIdentity) => Effect.Effect<void>; | |
| 43 | 46 | readonly respond: ( |
| 44 | 47 | response: PreviewAutomationResponse, |
| 45 | 48 | ) => Effect.Effect<void, PreviewAutomationError>; |
| @@ -63,7 +66,7 @@ interface ClientConnection { · 17 unmodified lines | ||
| 63 | 66 | } |
| 64 | 67 | |
| 65 | 68 | interface PendingRequest { |
| 66 | − readonly clientId: string; | |
| 69 | + readonly queue: ClientConnection["queue"]; | |
| 67 | 70 | readonly deferred: Deferred.Deferred<unknown, PreviewAutomationError>; |
| 68 | 71 | } |
| 69 | 72 | |
| @@ -133,17 +136,16 @@ const make = Effect.gen(function* PreviewAutomationBrokerMake() { · 63 unmodified lines | ||
| 133 | 136 | queue: ClientConnection["queue"], |
| 134 | 137 | ) { |
| 135 | 138 | const toFail = yield* SynchronizedRef.modify(state, (current) => { |
| 136 | − if (current.clients.get(clientId)?.queue !== queue) { | |
| 137 | − return [[] as ReadonlyArray<PendingRequest>, current] as const; | |
| 138 | − } | |
| 139 | 139 | const clients = new Map(current.clients); |
| 140 | 140 | const owners = new Map(current.owners); |
| 141 | 141 | const pending = new Map(current.pending); |
| 142 | 142 | const disconnected: PendingRequest[] = []; |
| 143 | − clients.delete(clientId); | |
| 144 | − owners.delete(clientId); | |
| 143 | + if (current.clients.get(clientId)?.queue === queue) { | |
| 144 | + clients.delete(clientId); | |
| 145 | + owners.delete(clientId); | |
| 146 | + } | |
| 145 | 147 | for (const [requestId, entry] of pending) { |
| 146 | − if (entry.clientId === clientId) { | |
| 148 | + if (entry.queue === queue) { | |
| 147 | 149 | pending.delete(requestId); |
| 148 | 150 | disconnected.push(entry); |
| 149 | 151 | } |
| @@ -166,12 +168,22 @@ const make = Effect.gen(function* PreviewAutomationBrokerMake() { · 16 unmodified lines | ||
| 166 | 168 | |
| 167 | 169 | const connect: PreviewAutomationBrokerShape["connect"] = Effect.fn( |
| 168 | 170 | "PreviewAutomationBroker.connect", |
| 169 | − )(function* (clientId) { | |
| 171 | + )(function* (owner) { | |
| 172 | + const clientId = owner.clientId; | |
| 170 | 173 | const queue = yield* Queue.unbounded<import("@t3tools/contracts").PreviewAutomationRequest>(); |
| 171 | 174 | const previous = yield* SynchronizedRef.modify(state, (current) => { |
| 172 | 175 | const clients = new Map(current.clients); |
| 176 | + const owners = new Map(current.owners); | |
| 177 | + const existingOwner = current.owners.get(clientId); | |
| 173 | 178 | clients.set(clientId, { clientId, queue }); |
| 174 | − return [current.clients.get(clientId), { ...current, clients }] as const; | |
| 179 | + owners.set( | |
| 180 | + clientId, | |
| 181 | + existingOwner?.environmentId === owner.environmentId && | |
| 182 | + existingOwner.threadId === owner.threadId | |
| 183 | + ? { ...existingOwner, supportsAutomation: owner.supportsAutomation } | |
| 184 | + : owner, | |
| 185 | + ); | |
| 186 | + return [current.clients.get(clientId), { ...current, clients, owners }] as const; | |
| 175 | 187 | }); |
| 176 | 188 | if (previous) yield* disconnect(clientId, previous.queue); |
| 177 | 189 | return Stream.fromQueue(queue).pipe(Stream.ensuring(disconnect(clientId, queue))); |
| @@ -189,10 +201,18 @@ const make = Effect.gen(function* PreviewAutomationBrokerMake() { · 11 unmodified lines | ||
| 189 | 201 | |
| 190 | 202 | const clearOwner: PreviewAutomationBrokerShape["clearOwner"] = Effect.fn( |
| 191 | 203 | "PreviewAutomationBroker.clearOwner", |
| 192 | − )(function* (clientId) { | |
| 204 | + )(function* (owner) { | |
| 193 | 205 | yield* SynchronizedRef.update(state, (current) => { |
| 206 | + const currentOwner = current.owners.get(owner.clientId); | |
| 207 | + if ( | |
| 208 | + !currentOwner || | |
| 209 | + currentOwner.environmentId !== owner.environmentId || | |
| 210 | + currentOwner.threadId !== owner.threadId | |
| 211 | + ) { | |
| 212 | + return current; | |
| 213 | + } | |
| 194 | 214 | const owners = new Map(current.owners); |
| 195 | − owners.delete(clientId); | |
| 215 | + owners.delete(owner.clientId); | |
| 196 | 216 | return { ...current, owners }; |
| 197 | 217 | }); |
| 198 | 218 | }); |
| @@ -234,8 +254,13 @@ const make = Effect.gen(function* PreviewAutomationBrokerMake() { · 35 unmodified lines | ||
| 234 | 254 | owner.supportsAutomation, |
| 235 | 255 | ) |
| 236 | 256 | .sort((left, right) => right.focusedAt.localeCompare(left.focusedAt)); |
| 237 | − const owner = candidates[0]; | |
| 257 | + const owner = candidates.find((candidate) => current.clients.has(candidate.clientId)); | |
| 238 | 258 | if (!owner) { |
| 259 | + if (candidates.length > 0) { | |
| 260 | + return yield* new PreviewAutomationUnavailableError({ | |
| 261 | + message: "The browser host is not connected.", | |
| 262 | + }); | |
| 263 | + } | |
| 239 | 264 | return yield* new PreviewAutomationNoFocusedOwnerError({ |
| 240 | 265 | message: "No desktop browser host is available for this thread.", |
| 241 | 266 | }); |
| @@ -246,22 +271,12 @@ const make = Effect.gen(function* PreviewAutomationBrokerMake() { · 4 unmodified lines | ||
| 246 | 271 | message: "The browser host is not connected.", |
| 247 | 272 | }); |
| 248 | 273 | } |
| 249 | − if ( | |
| 250 | − input.operation !== "open" && | |
| 251 | − input.operation !== "status" && | |
| 252 | − !owner.tabId && | |
| 253 | − !input.tabId | |
| 254 | − ) { | |
| 255 | − return yield* new PreviewAutomationTabNotFoundError({ | |
| 256 | − message: "The browser host does not have an active tab.", | |
| 257 | − }); | |
| 258 | − } | |
| 259 | 274 | const timeoutMs = input.timeoutMs ?? 15_000; |
| 260 | 275 | const deferred = yield* Deferred.make<unknown, PreviewAutomationError>(); |
| 261 | 276 | const requestId = yield* SynchronizedRef.modify(state, (next) => { |
| 262 | 277 | const requestId = `preview-${next.requestSequence}`; |
| 263 | 278 | const pending = new Map(next.pending); |
| 264 | − pending.set(requestId, { clientId: owner.clientId, deferred }); | |
| 279 | + pending.set(requestId, { queue: connection.queue, deferred }); | |
| 265 | 280 | return [requestId, { ...next, pending, requestSequence: next.requestSequence + 1 }] as const; |
| 266 | 281 | }); |
| 267 | 282 | const removePending = SynchronizedRef.update(state, (next) => { |
| @@ -32,8 +32,8 @@ import { applyServerSettingsPatch } from "@t3tools/shared/serverSettings"; · 31 unmodified lines | ||
| 32 | 32 | |
| 33 | 33 | import { checkCodexProviderStatus, type CodexAppServerProviderSnapshot } from "./CodexProvider.ts"; |
| 34 | 34 | import { checkClaudeProviderStatus } from "./ClaudeProvider.ts"; |
| 35 | −import { OpenCodeRuntimeLive } from "../opencodeRuntime.ts"; | |
| 36 | −import { NoOpProviderEventLoggers, ProviderEventLoggers } from "./ProviderEventLoggers.ts"; | |
| 35 | +import * as OpenCodeRuntime from "../opencodeRuntime.ts"; | |
| 36 | +import * as ProviderEventLoggers from "./ProviderEventLoggers.ts"; | |
| 37 | 37 | import { ProviderInstanceRegistryHydrationLive } from "./ProviderInstanceRegistryHydration.ts"; |
| 38 | 38 | import { |
| 39 | 39 | haveProvidersChanged, |
| @@ -42,12 +42,12 @@ import { · 2 unmodified lines | ||
| 42 | 42 | ProviderRegistryLive, |
| 43 | 43 | selectProvidersByKind, |
| 44 | 44 | } from "./ProviderRegistry.ts"; |
| 45 | −import { ServerConfig } from "../../config.ts"; | |
| 46 | −import { ServerSettingsService, type ServerSettingsShape } from "../../serverSettings.ts"; | |
| 45 | +import * as ServerConfig from "../../config.ts"; | |
| 46 | +import * as ServerSettingsModule from "../../serverSettings.ts"; | |
| 47 | 47 | import { readProviderStatusCache, resolveProviderStatusCachePath } from "../providerStatusCache.ts"; |
| 48 | 48 | import type { ProviderInstance } from "../ProviderDriver.ts"; |
| 49 | −import { ProviderInstanceRegistry } from "../Services/ProviderInstanceRegistry.ts"; | |
| 50 | −import { ProviderRegistry } from "../Services/ProviderRegistry.ts"; | |
| 49 | +import * as ProviderInstanceRegistry from "../Services/ProviderInstanceRegistry.ts"; | |
| 50 | +import * as ProviderRegistry from "../Services/ProviderRegistry.ts"; | |
| 51 | 51 | import { makeManualOnlyProviderMaintenanceCapabilities } from "../providerMaintenance.ts"; |
| 52 | 52 | const decodeServerSettings = Schema.decodeSync(ServerSettings); |
| 53 | 53 | const encodeServerSettings = Schema.encodeSync(ServerSettings); |
| @@ -294,11 +294,11 @@ function makeMutableServerSettingsService( · 240 unmodified lines | ||
| 294 | 294 | get streamChanges() { |
| 295 | 295 | return Stream.fromPubSub(changes); |
| 296 | 296 | }, |
| 297 | − } satisfies ServerSettingsShape; | |
| 297 | + } satisfies ServerSettingsModule.ServerSettingsService["Service"]; | |
| 298 | 298 | }); |
| 299 | 299 | } |
| 300 | 300 | |
| 301 | −it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), TestHttpClientLive))( | |
| 301 | +it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsModule.layerTest(), TestHttpClientLive))( | |
| 302 | 302 | "ProviderRegistry", |
| 303 | 303 | (it) => { |
| 304 | 304 | describe("checkCodexProviderStatus", () => { |
| @@ -636,14 +636,17 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 331 unmodified lines | ||
| 636 | 636 | adapter: {} as ProviderInstance["adapter"], |
| 637 | 637 | textGeneration: {} as ProviderInstance["textGeneration"], |
| 638 | 638 | } satisfies ProviderInstance; |
| 639 | − const instanceRegistryLayer = Layer.succeed(ProviderInstanceRegistry, { | |
| 640 | − getInstance: (instanceId) => | |
| 641 | − Effect.succeed(instanceId === codexInstanceId ? instance : undefined), | |
| 642 | − listInstances: Effect.succeed([instance]), | |
| 643 | − listUnavailable: Effect.succeed([]), | |
| 644 | − streamChanges: Stream.empty, | |
| 645 | − subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), PubSub.subscribe), | |
| 646 | − }); | |
| 639 | + const instanceRegistryLayer = Layer.succeed( | |
| 640 | + ProviderInstanceRegistry.ProviderInstanceRegistry, | |
| 641 | + { | |
| 642 | + getInstance: (instanceId) => | |
| 643 | + Effect.succeed(instanceId === codexInstanceId ? instance : undefined), | |
| 644 | + listInstances: Effect.succeed([instance]), | |
| 645 | + listUnavailable: Effect.succeed([]), | |
| 646 | + streamChanges: Stream.empty, | |
| 647 | + subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), PubSub.subscribe), | |
| 648 | + }, | |
| 649 | + ); | |
| 647 | 650 | const scope = yield* Scope.make(); |
| 648 | 651 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); |
| 649 | 652 | const runtimeServices = yield* Layer.build( |
| @@ -658,7 +661,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 8 unmodified lines | ||
| 658 | 661 | ), |
| 659 | 662 | ).pipe(Scope.provide(scope)); |
| 660 | 663 | yield* Effect.gen(function* () { |
| 661 | − const registry = yield* ProviderRegistry; | |
| 664 | + const registry = yield* ProviderRegistry.ProviderRegistry; | |
| 662 | 665 | assert.deepStrictEqual(yield* registry.getProviders, [initialProvider]); |
| 663 | 666 | assert.strictEqual(yield* Ref.get(refreshCalls), 0); |
| 664 | 667 | }).pipe(Effect.provide(runtimeServices)); |
| @@ -786,16 +789,19 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 121 unmodified lines | ||
| 786 | 789 | adapter: {} as ProviderInstance["adapter"], |
| 787 | 790 | textGeneration: {} as ProviderInstance["textGeneration"], |
| 788 | 791 | } satisfies ProviderInstance; |
| 789 | − const instanceRegistryLayer = Layer.succeed(ProviderInstanceRegistry, { | |
| 790 | − getInstance: (instanceId) => | |
| 791 | − Effect.succeed(instanceId === cursorInstanceId ? instance : undefined), | |
| 792 | − listInstances: Effect.succeed([instance]), | |
| 793 | − listUnavailable: Effect.succeed([]), | |
| 794 | − streamChanges: Stream.empty, | |
| 795 | − subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => | |
| 796 | − PubSub.subscribe(pubsub), | |
| 797 | − ), | |
| 798 | − }); | |
| 792 | + const instanceRegistryLayer = Layer.succeed( | |
| 793 | + ProviderInstanceRegistry.ProviderInstanceRegistry, | |
| 794 | + { | |
| 795 | + getInstance: (instanceId) => | |
| 796 | + Effect.succeed(instanceId === cursorInstanceId ? instance : undefined), | |
| 797 | + listInstances: Effect.succeed([instance]), | |
| 798 | + listUnavailable: Effect.succeed([]), | |
| 799 | + streamChanges: Stream.empty, | |
| 800 | + subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => | |
| 801 | + PubSub.subscribe(pubsub), | |
| 802 | + ), | |
| 803 | + }, | |
| 804 | + ); | |
| 799 | 805 | const scope = yield* Scope.make(); |
| 800 | 806 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); |
| 801 | 807 | const runtimeServices = yield* Layer.build( |
| @@ -811,8 +817,8 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 9 unmodified lines | ||
| 811 | 817 | ).pipe(Scope.provide(scope)); |
| 812 | 818 | |
| 813 | 819 | yield* Effect.gen(function* () { |
| 814 | − const registry = yield* ProviderRegistry; | |
| 815 | − const config = yield* ServerConfig; | |
| 820 | + const registry = yield* ProviderRegistry.ProviderRegistry; | |
| 821 | + const config = yield* ServerConfig.ServerConfig; | |
| 816 | 822 | const filePath = yield* resolveProviderStatusCachePath({ |
| 817 | 823 | cacheDir: config.providerStatusCacheDir, |
| 818 | 824 | instanceId: cursorInstanceId, |
| @@ -880,16 +886,19 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 61 unmodified lines | ||
| 880 | 886 | adapter: {} as ProviderInstance["adapter"], |
| 881 | 887 | textGeneration: {} as ProviderInstance["textGeneration"], |
| 882 | 888 | } satisfies ProviderInstance; |
| 883 | − const instanceRegistryLayer = Layer.succeed(ProviderInstanceRegistry, { | |
| 884 | − getInstance: (instanceId) => | |
| 885 | − Effect.succeed(instanceId === codexInstanceId ? instance : undefined), | |
| 886 | − listInstances: Effect.succeed([instance]), | |
| 887 | − listUnavailable: Effect.succeed([]), | |
| 888 | − streamChanges: Stream.empty, | |
| 889 | − subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => | |
| 890 | − PubSub.subscribe(pubsub), | |
| 891 | − ), | |
| 892 | − }); | |
| 889 | + const instanceRegistryLayer = Layer.succeed( | |
| 890 | + ProviderInstanceRegistry.ProviderInstanceRegistry, | |
| 891 | + { | |
| 892 | + getInstance: (instanceId) => | |
| 893 | + Effect.succeed(instanceId === codexInstanceId ? instance : undefined), | |
| 894 | + listInstances: Effect.succeed([instance]), | |
| 895 | + listUnavailable: Effect.succeed([]), | |
| 896 | + streamChanges: Stream.empty, | |
| 897 | + subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => | |
| 898 | + PubSub.subscribe(pubsub), | |
| 899 | + ), | |
| 900 | + }, | |
| 901 | + ); | |
| 893 | 902 | const scope = yield* Scope.make(); |
| 894 | 903 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); |
| 895 | 904 | const runtimeServices = yield* Layer.build( |
| @@ -905,7 +914,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 9 unmodified lines | ||
| 905 | 914 | ).pipe(Scope.provide(scope)); |
| 906 | 915 | |
| 907 | 916 | yield* Effect.gen(function* () { |
| 908 | − const registry = yield* ProviderRegistry; | |
| 917 | + const registry = yield* ProviderRegistry.ProviderRegistry; | |
| 909 | 918 | |
| 910 | 919 | assert.deepStrictEqual(yield* registry.getProviders, [cachedProvider]); |
| 911 | 920 | assert.deepStrictEqual(yield* registry.refresh(codexDriver), [cachedProvider]); |
| @@ -975,25 +984,28 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 63 unmodified lines | ||
| 975 | 984 | const instancesRef = yield* Ref.make<ReadonlyArray<ProviderInstance>>([codexInstance]); |
| 976 | 985 | const failNextList = yield* Ref.make(false); |
| 977 | 986 | const wait = () => Effect.yieldNow; |
| 978 | − const instanceRegistryLayer = Layer.succeed(ProviderInstanceRegistry, { | |
| 979 | − getInstance: (instanceId) => | |
| 980 | − Ref.get(instancesRef).pipe( | |
| 981 | − Effect.map((instances) => | |
| 982 | − instances.find((instance) => instance.instanceId === instanceId), | |
| 987 | + const instanceRegistryLayer = Layer.succeed( | |
| 988 | + ProviderInstanceRegistry.ProviderInstanceRegistry, | |
| 989 | + { | |
| 990 | + getInstance: (instanceId) => | |
| 991 | + Ref.get(instancesRef).pipe( | |
| 992 | + Effect.map((instances) => | |
| 993 | + instances.find((instance) => instance.instanceId === instanceId), | |
| 994 | + ), | |
| 983 | 995 | ), |
| 984 | − ), | |
| 985 | − listInstances: Effect.gen(function* () { | |
| 986 | − const shouldFail = yield* Ref.get(failNextList); | |
| 987 | − if (shouldFail) { | |
| 988 | − yield* Ref.set(failNextList, false); | |
| 989 | − return yield* Effect.die(new Error("simulated registry list failure")); | |
| 990 | − } | |
| 991 | − return yield* Ref.get(instancesRef); | |
| 992 | − }), | |
| 993 | − listUnavailable: Effect.succeed([]), | |
| 994 | − streamChanges: Stream.fromPubSub(changes), | |
| 995 | − subscribeChanges: PubSub.subscribe(changes), | |
| 996 | − }); | |
| 996 | + listInstances: Effect.gen(function* () { | |
| 997 | + const shouldFail = yield* Ref.get(failNextList); | |
| 998 | + if (shouldFail) { | |
| 999 | + yield* Ref.set(failNextList, false); | |
| 1000 | + return yield* Effect.die(new Error("simulated registry list failure")); | |
| 1001 | + } | |
| 1002 | + return yield* Ref.get(instancesRef); | |
| 1003 | + }), | |
| 1004 | + listUnavailable: Effect.succeed([]), | |
| 1005 | + streamChanges: Stream.fromPubSub(changes), | |
| 1006 | + subscribeChanges: PubSub.subscribe(changes), | |
| 1007 | + }, | |
| 1008 | + ); | |
| 997 | 1009 | const scope = yield* Scope.make(); |
| 998 | 1010 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); |
| 999 | 1011 | const runtimeServices = yield* Layer.build( |
| @@ -1009,7 +1021,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 9 unmodified lines | ||
| 1009 | 1021 | ).pipe(Scope.provide(scope)); |
| 1010 | 1022 | |
| 1011 | 1023 | yield* Effect.gen(function* () { |
| 1012 | − const registry = yield* ProviderRegistry; | |
| 1024 | + const registry = yield* ProviderRegistry.ProviderRegistry; | |
| 1013 | 1025 | assert.deepStrictEqual(yield* registry.getProviders, [codexProvider]); |
| 1014 | 1026 | |
| 1015 | 1027 | yield* Ref.set(failNextList, true); |
| @@ -1092,15 +1104,22 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 76 unmodified lines | ||
| 1092 | 1104 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); |
| 1093 | 1105 | const providerRegistryLayer = ProviderRegistryLive.pipe( |
| 1094 | 1106 | Layer.provideMerge(ProviderInstanceRegistryHydrationLive), |
| 1095 | − Layer.provideMerge(Layer.succeed(ServerSettingsService, serverSettings)), | |
| 1107 | + Layer.provideMerge( | |
| 1108 | + Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | |
| 1109 | + ), | |
| 1096 | 1110 | Layer.provideMerge( |
| 1097 | 1111 | ServerConfig.layerTest(process.cwd(), { |
| 1098 | 1112 | prefix: "t3-provider-registry-", |
| 1099 | 1113 | }), |
| 1100 | 1114 | ), |
| 1101 | 1115 | Layer.provideMerge(TestHttpClientLive), |
| 1102 | − Layer.provideMerge(Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers)), | |
| 1103 | − Layer.provideMerge(OpenCodeRuntimeLive), | |
| 1116 | + Layer.provideMerge( | |
| 1117 | + Layer.succeed( | |
| 1118 | + ProviderEventLoggers.ProviderEventLoggers, | |
| 1119 | + ProviderEventLoggers.NoOpProviderEventLoggers, | |
| 1120 | + ), | |
| 1121 | + ), | |
| 1122 | + Layer.provideMerge(OpenCodeRuntime.OpenCodeRuntimeLive), | |
| 1104 | 1123 | // NO spawner mock — `ChildProcessSpawner` is supplied by the |
| 1105 | 1124 | // outer `NodeServices.layer` on `it.layer(...)` and will |
| 1106 | 1125 | // genuinely spawn a subprocess. The missing-binary ENOENT is |
| @@ -1112,7 +1131,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 5 unmodified lines | ||
| 1112 | 1131 | ); |
| 1113 | 1132 | |
| 1114 | 1133 | yield* Effect.gen(function* () { |
| 1115 | − const registry = yield* ProviderRegistry; | |
| 1134 | + const registry = yield* ProviderRegistry.ProviderRegistry; | |
| 1116 | 1135 | let providers = yield* registry.getProviders; |
| 1117 | 1136 | for ( |
| 1118 | 1137 | let attempts = 0; |
| @@ -1177,15 +1196,22 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 58 unmodified lines | ||
| 1177 | 1196 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); |
| 1178 | 1197 | const providerRegistryLayer = ProviderRegistryLive.pipe( |
| 1179 | 1198 | Layer.provideMerge(ProviderInstanceRegistryHydrationLive), |
| 1180 | − Layer.provideMerge(Layer.succeed(ServerSettingsService, serverSettings)), | |
| 1199 | + Layer.provideMerge( | |
| 1200 | + Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | |
| 1201 | + ), | |
| 1181 | 1202 | Layer.provideMerge( |
| 1182 | 1203 | ServerConfig.layerTest(process.cwd(), { |
| 1183 | 1204 | prefix: "t3-provider-registry-", |
| 1184 | 1205 | }), |
| 1185 | 1206 | ), |
| 1186 | 1207 | Layer.provideMerge(TestHttpClientLive), |
| 1187 | − Layer.provideMerge(Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers)), | |
| 1188 | − Layer.provideMerge(OpenCodeRuntimeLive), | |
| 1208 | + Layer.provideMerge( | |
| 1209 | + Layer.succeed( | |
| 1210 | + ProviderEventLoggers.ProviderEventLoggers, | |
| 1211 | + ProviderEventLoggers.NoOpProviderEventLoggers, | |
| 1212 | + ), | |
| 1213 | + ), | |
| 1214 | + Layer.provideMerge(OpenCodeRuntime.OpenCodeRuntimeLive), | |
| 1189 | 1215 | Layer.updateService(ChildProcessSpawner.ChildProcessSpawner, (spawner) => |
| 1190 | 1216 | ChildProcessSpawner.make((command) => { |
| 1191 | 1217 | spawnedCommands.push((command as { readonly command: string }).command); |
| @@ -1199,7 +1225,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 7 unmodified lines | ||
| 1199 | 1225 | ); |
| 1200 | 1226 | |
| 1201 | 1227 | yield* Effect.gen(function* () { |
| 1202 | − const registry = yield* ProviderRegistry; | |
| 1228 | + const registry = yield* ProviderRegistry.ProviderRegistry; | |
| 1203 | 1229 | // Boot-time probe: the default codex instance is enabled with |
| 1204 | 1230 | // `firstMissing`, so the real spawner yields ENOENT and the |
| 1205 | 1231 | // snapshot should be `status: "error"`. |
| @@ -1291,15 +1317,22 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 85 unmodified lines | ||
| 1291 | 1317 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); |
| 1292 | 1318 | const providerRegistryLayer = ProviderRegistryLive.pipe( |
| 1293 | 1319 | Layer.provideMerge(ProviderInstanceRegistryHydrationLive), |
| 1294 | − Layer.provideMerge(Layer.succeed(ServerSettingsService, serverSettings)), | |
| 1320 | + Layer.provideMerge( | |
| 1321 | + Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | |
| 1322 | + ), | |
| 1295 | 1323 | Layer.provideMerge( |
| 1296 | 1324 | ServerConfig.layerTest(process.cwd(), { |
| 1297 | 1325 | prefix: "t3-provider-registry-", |
| 1298 | 1326 | }), |
| 1299 | 1327 | ), |
| 1300 | 1328 | Layer.provideMerge(TestHttpClientLive), |
| 1301 | − Layer.provideMerge(Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers)), | |
| 1302 | − Layer.provideMerge(OpenCodeRuntimeLive), | |
| 1329 | + Layer.provideMerge( | |
| 1330 | + Layer.succeed( | |
| 1331 | + ProviderEventLoggers.ProviderEventLoggers, | |
| 1332 | + ProviderEventLoggers.NoOpProviderEventLoggers, | |
| 1333 | + ), | |
| 1334 | + ), | |
| 1335 | + Layer.provideMerge(OpenCodeRuntime.OpenCodeRuntimeLive), | |
| 1303 | 1336 | Layer.provideMerge(NodeServices.layer), |
| 1304 | 1337 | ); |
| 1305 | 1338 | const runtimeServices = yield* Layer.build(providerRegistryLayer).pipe( |
| @@ -1307,7 +1340,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 1 unmodified line | ||
| 1307 | 1340 | ); |
| 1308 | 1341 | |
| 1309 | 1342 | yield* Effect.gen(function* () { |
| 1310 | − const registry = yield* ProviderRegistry; | |
| 1343 | + const registry = yield* ProviderRegistry.ProviderRegistry; | |
| 1311 | 1344 | const providers = yield* registry.getProviders; |
| 1312 | 1345 | const ghost = providers.find((provider) => provider.instanceId === "ghost_main"); |
| 1313 | 1346 | |
| @@ -1345,15 +1378,22 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 31 unmodified lines | ||
| 1345 | 1378 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); |
| 1346 | 1379 | const providerRegistryLayer = ProviderRegistryLive.pipe( |
| 1347 | 1380 | Layer.provideMerge(ProviderInstanceRegistryHydrationLive), |
| 1348 | − Layer.provideMerge(Layer.succeed(ServerSettingsService, serverSettings)), | |
| 1381 | + Layer.provideMerge( | |
| 1382 | + Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | |
| 1383 | + ), | |
| 1349 | 1384 | Layer.provideMerge( |
| 1350 | 1385 | ServerConfig.layerTest(process.cwd(), { |
| 1351 | 1386 | prefix: "t3-provider-registry-", |
| 1352 | 1387 | }), |
| 1353 | 1388 | ), |
| 1354 | 1389 | Layer.provideMerge(TestHttpClientLive), |
| 1355 | − Layer.provideMerge(Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers)), | |
| 1356 | − Layer.provideMerge(OpenCodeRuntimeLive), | |
| 1390 | + Layer.provideMerge( | |
| 1391 | + Layer.succeed( | |
| 1392 | + ProviderEventLoggers.ProviderEventLoggers, | |
| 1393 | + ProviderEventLoggers.NoOpProviderEventLoggers, | |
| 1394 | + ), | |
| 1395 | + ), | |
| 1396 | + Layer.provideMerge(OpenCodeRuntime.OpenCodeRuntimeLive), | |
| 1357 | 1397 | Layer.provideMerge( |
| 1358 | 1398 | mockCommandSpawnerLayer((command, args) => { |
| 1359 | 1399 | if (command === "agent") { |
| @@ -1380,13 +1420,13 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest(), T · 20 unmodified lines | ||
| 1380 | 1420 | ); |
| 1381 | 1421 | const runtimeServices = yield* Layer.build( |
| 1382 | 1422 | Layer.mergeAll( |
| 1383 | − Layer.succeed(ServerSettingsService, serverSettings), | |
| 1423 | + Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | |
| 1384 | 1424 | providerRegistryLayer, |
| 1385 | 1425 | ), |
| 1386 | 1426 | ).pipe(Scope.provide(scope)); |
| 1387 | 1427 | |
| 1388 | 1428 | yield* Effect.gen(function* () { |
| 1389 | − const registry = yield* ProviderRegistry; | |
| 1429 | + const registry = yield* ProviderRegistry.ProviderRegistry; | |
| 1390 | 1430 | const providers = yield* registry.getProviders; |
| 1391 | 1431 | const cursorProvider = providers.find( |
| 1392 | 1432 | (provider) => provider.instanceId === ProviderInstanceId.make("cursor"), |
| @@ -3,7 +3,7 @@ import * as Effect from "effect/Effect"; · 2 unmodified lines | ||
| 3 | 3 | import * as Equal from "effect/Equal"; |
| 4 | 4 | import * as Stream from "effect/Stream"; |
| 5 | 5 | |
| 6 | −import type { ServerSettingsShape } from "../serverSettings.ts"; | |
| 6 | +import type * as ServerSettingsModule from "../serverSettings.ts"; | |
| 7 | 7 | |
| 8 | 8 | export interface ProviderSnapshotSettings<Settings> { |
| 9 | 9 | readonly provider: Settings; |
| @@ -29,7 +29,7 @@ export function haveProviderSnapshotSettingsChanged<Settings>( · 19 unmodified lines | ||
| 29 | 29 | |
| 30 | 30 | export function makeProviderSnapshotSettingsSource<Settings>( |
| 31 | 31 | provider: Settings, |
| 32 | − serverSettings: ServerSettingsShape, | |
| 32 | + serverSettings: ServerSettingsModule.ServerSettingsService["Service"], | |
| 33 | 33 | ): { |
| 34 | 34 | readonly getSettings: Effect.Effect<ProviderSnapshotSettings<Settings>, ServerSettingsError>; |
| 35 | 35 | readonly streamSettings: Stream.Stream<ProviderSnapshotSettings<Settings>>; |
| @@ -69,56 +69,30 @@ import { vi } from "vite-plus/test"; · 68 unmodified lines | ||
| 69 | 69 | |
| 70 | 70 | const TEST_EPOCH = DateTime.makeUnsafe("1970-01-01T00:00:00.000Z"); |
| 71 | 71 | |
| 72 | −import type { ServerConfigShape } from "./config.ts"; | |
| 73 | −import { deriveServerPaths, ServerConfig } from "./config.ts"; | |
| 72 | +import * as ServerConfig from "./config.ts"; | |
| 74 | 73 | import { makeRoutesLayer } from "./server.ts"; |
| 75 | −import { | |
| 76 | − CheckpointDiffQuery, | |
| 77 | − type CheckpointDiffQueryShape, | |
| 78 | −} from "./checkpointing/Services/CheckpointDiffQuery.ts"; | |
| 79 | −import { GitManager, type GitManagerShape } from "./git/GitManager.ts"; | |
| 80 | −import { Keybindings, type KeybindingsShape } from "./keybindings.ts"; | |
| 74 | +import * as CheckpointDiffQuery from "./checkpointing/Services/CheckpointDiffQuery.ts"; | |
| 75 | +import * as GitManager from "./git/GitManager.ts"; | |
| 76 | +import * as Keybindings from "./keybindings.ts"; | |
| 81 | 77 | import * as ExternalLauncher from "./process/externalLauncher.ts"; |
| 82 | −import { | |
| 83 | − OrchestrationEngineService, | |
| 84 | − type OrchestrationEngineShape, | |
| 85 | −} from "./orchestration/Services/OrchestrationEngine.ts"; | |
| 78 | +import * as OrchestrationEngine from "./orchestration/Services/OrchestrationEngine.ts"; | |
| 86 | 79 | import { OrchestrationListenerCallbackError } from "./orchestration/Errors.ts"; |
| 87 | −import { | |
| 88 | − ProjectionSnapshotQuery, | |
| 89 | − type ProjectionSnapshotQueryShape, | |
| 90 | −} from "./orchestration/Services/ProjectionSnapshotQuery.ts"; | |
| 80 | +import * as ProjectionSnapshotQuery from "./orchestration/Services/ProjectionSnapshotQuery.ts"; | |
| 91 | 81 | import { SqlitePersistenceMemory } from "./persistence/Layers/Sqlite.ts"; |
| 92 | 82 | import { PersistenceSqlError } from "./persistence/Errors.ts"; |
| 93 | −import { | |
| 94 | − ProviderRegistry, | |
| 95 | − type ProviderRegistryShape, | |
| 96 | −} from "./provider/Services/ProviderRegistry.ts"; | |
| 83 | +import * as ProviderRegistry from "./provider/Services/ProviderRegistry.ts"; | |
| 97 | 84 | import { makeManualOnlyProviderMaintenanceCapabilities } from "./provider/providerMaintenance.ts"; |
| 98 | −import { ServerLifecycleEvents, type ServerLifecycleEventsShape } from "./serverLifecycleEvents.ts"; | |
| 99 | −import { ServerRuntimeStartup, type ServerRuntimeStartupShape } from "./serverRuntimeStartup.ts"; | |
| 100 | −import { ServerSettingsService, type ServerSettingsShape } from "./serverSettings.ts"; | |
| 101 | −import { TerminalManager, type TerminalManagerShape } from "./terminal/Services/Manager.ts"; | |
| 85 | +import * as ServerLifecycleEvents from "./serverLifecycleEvents.ts"; | |
| 86 | +import * as ServerRuntimeStartup from "./serverRuntimeStartup.ts"; | |
| 87 | +import * as ServerSettings from "./serverSettings.ts"; | |
| 88 | +import * as TerminalManager from "./terminal/Services/Manager.ts"; | |
| 102 | 89 | import * as PreviewManager from "./preview/Manager.ts"; |
| 103 | 90 | import * as PortScanner from "./preview/PortScanner.ts"; |
| 104 | −import { | |
| 105 | − BrowserTraceCollector, | |
| 106 | − type BrowserTraceCollectorShape, | |
| 107 | −} from "./observability/Services/BrowserTraceCollector.ts"; | |
| 91 | +import * as BrowserTraceCollector from "./observability/Services/BrowserTraceCollector.ts"; | |
| 108 | 92 | import { ProjectFaviconResolverLive } from "./project/Layers/ProjectFaviconResolver.ts"; |
| 109 | −import { | |
| 110 | − ProjectSetupScriptRunner, | |
| 111 | − ProjectSetupScriptRunnerError, | |
| 112 | − type ProjectSetupScriptRunnerShape, | |
| 113 | −} from "./project/Services/ProjectSetupScriptRunner.ts"; | |
| 114 | −import { | |
| 115 | − RepositoryIdentityResolver, | |
| 116 | − type RepositoryIdentityResolverShape, | |
| 117 | −} from "./project/Services/RepositoryIdentityResolver.ts"; | |
| 118 | −import { | |
| 119 | − ServerEnvironment, | |
| 120 | − type ServerEnvironmentShape, | |
| 121 | −} from "./environment/Services/ServerEnvironment.ts"; | |
| 93 | +import * as ProjectSetupScriptRunner from "./project/Services/ProjectSetupScriptRunner.ts"; | |
| 94 | +import * as RepositoryIdentityResolver from "./project/Services/RepositoryIdentityResolver.ts"; | |
| 95 | +import * as ServerEnvironment from "./environment/Services/ServerEnvironment.ts"; | |
| 122 | 96 | import * as WorkspaceEntries from "./workspace/WorkspaceEntries.ts"; |
| 123 | 97 | import { WorkspaceFileSystemLive } from "./workspace/Layers/WorkspaceFileSystem.ts"; |
| 124 | 98 | import { WorkspacePathsLive } from "./workspace/Layers/WorkspacePaths.ts"; |
| @@ -132,10 +106,7 @@ import * as ReviewService from "./review/ReviewService.ts"; · 7 unmodified lines | ||
| 132 | 106 | import * as SourceControlRepositoryService from "./sourceControl/SourceControlRepositoryService.ts"; |
| 133 | 107 | import * as ServerSecretStore from "./auth/ServerSecretStore.ts"; |
| 134 | 108 | import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts"; |
| 135 | −import { | |
| 136 | − CloudManagedEndpointRuntime, | |
| 137 | − type CloudManagedEndpointRuntimeShape, | |
| 138 | −} from "./cloud/ManagedEndpointRuntime.ts"; | |
| 109 | +import * as CloudManagedEndpointRuntime from "./cloud/ManagedEndpointRuntime.ts"; | |
| 139 | 110 | import * as CloudCliTokenManager from "./cloud/CliTokenManager.ts"; |
| 140 | 111 | import * as ProcessDiagnostics from "./diagnostics/ProcessDiagnostics.ts"; |
| 141 | 112 | import * as ProcessResourceMonitor from "./diagnostics/ProcessResourceMonitor.ts"; |
| @@ -341,32 +312,40 @@ const makeBrowserOtlpPayload = (spanName: string) => · 199 unmodified lines | ||
| 341 | 312 | }); |
| 342 | 313 | |
| 343 | 314 | const buildAppUnderTest = (options?: { |
| 344 | − config?: Partial<ServerConfigShape>; | |
| 315 | + config?: Partial<ServerConfig.ServerConfig["Service"]>; | |
| 345 | 316 | layers?: { |
| 346 | − keybindings?: Partial<KeybindingsShape>; | |
| 347 | − providerRegistry?: Partial<ProviderRegistryShape>; | |
| 348 | − serverSettings?: Partial<ServerSettingsShape>; | |
| 349 | − externalLauncher?: Partial<ExternalLauncher.ExternalLauncherShape>; | |
| 350 | − vcsDriver?: Partial<VcsDriver.VcsDriverShape>; | |
| 351 | − vcsDriverRegistry?: Partial<VcsDriverRegistry.VcsDriverRegistryShape>; | |
| 352 | − gitVcsDriver?: Partial<GitVcsDriver.GitVcsDriverShape>; | |
| 353 | − gitManager?: Partial<GitManagerShape>; | |
| 354 | − sourceControlRepositoryService?: Partial<SourceControlRepositoryService.SourceControlRepositoryServiceShape>; | |
| 355 | − reviewService?: Partial<ReviewService.ReviewServiceShape>; | |
| 356 | − vcsStatusBroadcaster?: Partial<VcsStatusBroadcaster.VcsStatusBroadcasterShape>; | |
| 357 | − projectSetupScriptRunner?: Partial<ProjectSetupScriptRunnerShape>; | |
| 358 | − terminalManager?: Partial<TerminalManagerShape>; | |
| 359 | − orchestrationEngine?: Partial<OrchestrationEngineShape>; | |
| 360 | − projectionSnapshotQuery?: Partial<ProjectionSnapshotQueryShape>; | |
| 361 | − checkpointDiffQuery?: Partial<CheckpointDiffQueryShape>; | |
| 362 | − browserTraceCollector?: Partial<BrowserTraceCollectorShape>; | |
| 363 | − serverLifecycleEvents?: Partial<ServerLifecycleEventsShape>; | |
| 364 | − serverRuntimeStartup?: Partial<ServerRuntimeStartupShape>; | |
| 365 | − serverEnvironment?: Partial<ServerEnvironmentShape>; | |
| 366 | − repositoryIdentityResolver?: Partial<RepositoryIdentityResolverShape>; | |
| 367 | − cloudManagedEndpointRuntime?: Partial<CloudManagedEndpointRuntimeShape>; | |
| 368 | − relayClient?: Partial<RelayClient.RelayClientShape>; | |
| 369 | − cloudCliTokenManager?: Partial<CloudCliTokenManager.CloudCliTokenManagerShape>; | |
| 317 | + keybindings?: Partial<Keybindings.Keybindings["Service"]>; | |
| 318 | + providerRegistry?: Partial<ProviderRegistry.ProviderRegistry["Service"]>; | |
| 319 | + serverSettings?: Partial<ServerSettings.ServerSettingsService["Service"]>; | |
| 320 | + externalLauncher?: Partial<ExternalLauncher.ExternalLauncher["Service"]>; | |
| 321 | + vcsDriver?: Partial<VcsDriver.VcsDriver["Service"]>; | |
| 322 | + vcsDriverRegistry?: Partial<VcsDriverRegistry.VcsDriverRegistry["Service"]>; | |
| 323 | + gitVcsDriver?: Partial<GitVcsDriver.GitVcsDriver["Service"]>; | |
| 324 | + gitManager?: Partial<GitManager.GitManager["Service"]>; | |
| 325 | + sourceControlRepositoryService?: Partial< | |
| 326 | + SourceControlRepositoryService.SourceControlRepositoryService["Service"] | |
| 327 | + >; | |
| 328 | + reviewService?: Partial<ReviewService.ReviewService["Service"]>; | |
| 329 | + vcsStatusBroadcaster?: Partial<VcsStatusBroadcaster.VcsStatusBroadcaster["Service"]>; | |
| 330 | + projectSetupScriptRunner?: Partial< | |
| 331 | + ProjectSetupScriptRunner.ProjectSetupScriptRunner["Service"] | |
| 332 | + >; | |
| 333 | + terminalManager?: Partial<TerminalManager.TerminalManager["Service"]>; | |
| 334 | + orchestrationEngine?: Partial<OrchestrationEngine.OrchestrationEngineService["Service"]>; | |
| 335 | + projectionSnapshotQuery?: Partial<ProjectionSnapshotQuery.ProjectionSnapshotQuery["Service"]>; | |
| 336 | + checkpointDiffQuery?: Partial<CheckpointDiffQuery.CheckpointDiffQuery["Service"]>; | |
| 337 | + browserTraceCollector?: Partial<BrowserTraceCollector.BrowserTraceCollector["Service"]>; | |
| 338 | + serverLifecycleEvents?: Partial<ServerLifecycleEvents.ServerLifecycleEvents["Service"]>; | |
| 339 | + serverRuntimeStartup?: Partial<ServerRuntimeStartup.ServerRuntimeStartup["Service"]>; | |
| 340 | + serverEnvironment?: Partial<ServerEnvironment.ServerEnvironment["Service"]>; | |
| 341 | + repositoryIdentityResolver?: Partial< | |
| 342 | + RepositoryIdentityResolver.RepositoryIdentityResolver["Service"] | |
| 343 | + >; | |
| 344 | + cloudManagedEndpointRuntime?: Partial< | |
| 345 | + CloudManagedEndpointRuntime.CloudManagedEndpointRuntime["Service"] | |
| 346 | + >; | |
| 347 | + relayClient?: Partial<RelayClient.RelayClient["Service"]>; | |
| 348 | + cloudCliTokenManager?: Partial<CloudCliTokenManager.CloudCliTokenManager["Service"]>; | |
| 370 | 349 | }; |
| 371 | 350 | }) => |
| 372 | 351 | Effect.gen(function* () { |
| @@ -374,8 +353,8 @@ const buildAppUnderTest = (options?: { · 1 unmodified line | ||
| 374 | 353 | const tempBaseDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-router-test-" }); |
| 375 | 354 | const baseDir = options?.config?.baseDir ?? tempBaseDir; |
| 376 | 355 | const devUrl = options?.config?.devUrl; |
| 377 | − const derivedPaths = yield* deriveServerPaths(baseDir, devUrl); | |
| 378 | − const config: ServerConfigShape = { | |
| 356 | + const derivedPaths = yield* ServerConfig.deriveServerPaths(baseDir, devUrl); | |
| 357 | + const config: ServerConfig.ServerConfig["Service"] = { | |
| 379 | 358 | logLevel: "Info", |
| 380 | 359 | traceMinLevel: "Info", |
| 381 | 360 | traceTimingEnabled: true, |
| @@ -403,8 +382,8 @@ const buildAppUnderTest = (options?: { · 21 unmodified lines | ||
| 403 | 382 | tailscaleServePort: 443, |
| 404 | 383 | ...options?.config, |
| 405 | 384 | }; |
| 406 | − const layerConfig = Layer.succeed(ServerConfig, config); | |
| 407 | − const defaultVcsDriver: VcsDriver.VcsDriverShape = { | |
| 385 | + const layerConfig = ServerConfig.layer(config); | |
| 386 | + const defaultVcsDriver: VcsDriver.VcsDriver["Service"] = { | |
| 408 | 387 | capabilities: { |
| 409 | 388 | kind: "git", |
| 410 | 389 | supportsWorktrees: true, |
| @@ -502,7 +481,7 @@ const buildAppUnderTest = (options?: { · 91 unmodified lines | ||
| 502 | 481 | const gitVcsDriverLayer = Layer.mock(GitVcsDriver.GitVcsDriver)({ |
| 503 | 482 | ...options?.layers?.gitVcsDriver, |
| 504 | 483 | }); |
| 505 | − const gitManagerLayer = Layer.mock(GitManager)({ | |
| 484 | + const gitManagerLayer = Layer.mock(GitManager.GitManager)({ | |
| 506 | 485 | ...options?.layers?.gitManager, |
| 507 | 486 | }); |
| 508 | 487 | const workspaceEntriesLayer = WorkspaceEntries.layer.pipe( |
| @@ -545,7 +524,7 @@ const buildAppUnderTest = (options?: { · 36 unmodified lines | ||
| 545 | 524 | disableLogger: true, |
| 546 | 525 | }).pipe( |
| 547 | 526 | Layer.provide( |
| 548 | − Layer.mock(Keybindings)({ | |
| 527 | + Layer.mock(Keybindings.Keybindings)({ | |
| 549 | 528 | loadConfigState: Effect.succeed({ |
| 550 | 529 | keybindings: [], |
| 551 | 530 | issues: [], |
| @@ -555,7 +534,7 @@ const buildAppUnderTest = (options?: { · 3 unmodified lines | ||
| 555 | 534 | }), |
| 556 | 535 | ), |
| 557 | 536 | Layer.provide( |
| 558 | − Layer.mock(ProviderRegistry)({ | |
| 537 | + Layer.mock(ProviderRegistry.ProviderRegistry)({ | |
| 559 | 538 | getProviders: Effect.succeed([]), |
| 560 | 539 | refresh: () => Effect.succeed([]), |
| 561 | 540 | refreshInstance: () => Effect.succeed([]), |
| @@ -569,7 +548,7 @@ const buildAppUnderTest = (options?: { · 7 unmodified lines | ||
| 569 | 548 | }), |
| 570 | 549 | ), |
| 571 | 550 | Layer.provide( |
| 572 | − Layer.mock(ServerSettingsService)({ | |
| 551 | + Layer.mock(ServerSettings.ServerSettingsService)({ | |
| 573 | 552 | start: Effect.void, |
| 574 | 553 | ready: Effect.void, |
| 575 | 554 | getSettings: Effect.succeed(DEFAULT_SERVER_SETTINGS), |
| @@ -658,13 +637,13 @@ const buildAppUnderTest = (options?: { · 82 unmodified lines | ||
| 658 | 637 | ), |
| 659 | 638 | Layer.provideMerge(vcsStatusBroadcasterLayer), |
| 660 | 639 | Layer.provide( |
| 661 | − Layer.mock(ProjectSetupScriptRunner)({ | |
| 640 | + Layer.mock(ProjectSetupScriptRunner.ProjectSetupScriptRunner)({ | |
| 662 | 641 | runForThread: () => Effect.succeed({ status: "no-script" as const }), |
| 663 | 642 | ...options?.layers?.projectSetupScriptRunner, |
| 664 | 643 | }), |
| 665 | 644 | ), |
| 666 | 645 | Layer.provide( |
| 667 | − Layer.mock(TerminalManager)({ | |
| 646 | + Layer.mock(TerminalManager.TerminalManager)({ | |
| 668 | 647 | ...options?.layers?.terminalManager, |
| 669 | 648 | }), |
| 670 | 649 | ), |
| @@ -692,7 +671,7 @@ const buildAppUnderTest = (options?: { · 21 unmodified lines | ||
| 692 | 671 | ), |
| 693 | 672 | ), |
| 694 | 673 | Layer.provide( |
| 695 | − Layer.mock(OrchestrationEngineService)({ | |
| 674 | + Layer.mock(OrchestrationEngine.OrchestrationEngineService)({ | |
| 696 | 675 | readEvents: () => Stream.empty, |
| 697 | 676 | dispatch: () => Effect.succeed({ sequence: 0 }), |
| 698 | 677 | streamDomainEvents: Stream.empty, |
| @@ -700,7 +679,7 @@ const buildAppUnderTest = (options?: { · 1 unmodified line | ||
| 700 | 679 | }), |
| 701 | 680 | ), |
| 702 | 681 | Layer.provide( |
| 703 | − Layer.mock(ProjectionSnapshotQuery)({ | |
| 682 | + Layer.mock(ProjectionSnapshotQuery.ProjectionSnapshotQuery)({ | |
| 704 | 683 | getCommandReadModel: () => Effect.succeed(makeDefaultOrchestrationReadModel()), |
| 705 | 684 | getSnapshot: () => Effect.succeed(makeDefaultOrchestrationReadModel()), |
| 706 | 685 | getShellSnapshot: () => |
| @@ -729,7 +708,7 @@ const buildAppUnderTest = (options?: { · 22 unmodified lines | ||
| 729 | 708 | }), |
| 730 | 709 | ), |
| 731 | 710 | Layer.provide( |
| 732 | − Layer.mock(CheckpointDiffQuery)({ | |
| 711 | + Layer.mock(CheckpointDiffQuery.CheckpointDiffQuery)({ | |
| 733 | 712 | getTurnDiff: () => |
| 734 | 713 | Effect.succeed({ |
| 735 | 714 | threadId: defaultThreadId, |
| @@ -751,13 +730,13 @@ const buildAppUnderTest = (options?: { · 15 unmodified lines | ||
| 751 | 730 | |
| 752 | 731 | const appLayer = servedRoutesLayer.pipe( |
| 753 | 732 | Layer.provide( |
| 754 | − Layer.mock(BrowserTraceCollector)({ | |
| 733 | + Layer.mock(BrowserTraceCollector.BrowserTraceCollector)({ | |
| 755 | 734 | record: () => Effect.void, |
| 756 | 735 | ...options?.layers?.browserTraceCollector, |
| 757 | 736 | }), |
| 758 | 737 | ), |
| 759 | 738 | Layer.provide( |
| 760 | − Layer.mock(ServerLifecycleEvents)({ | |
| 739 | + Layer.mock(ServerLifecycleEvents.ServerLifecycleEvents)({ | |
| 761 | 740 | publish: (event) => Effect.succeed({ ...(event as any), sequence: 1 }), |
| 762 | 741 | snapshot: Effect.succeed({ sequence: 0, events: [] }), |
| 763 | 742 | stream: Stream.empty, |
| @@ -765,7 +744,7 @@ const buildAppUnderTest = (options?: { · 1 unmodified line | ||
| 765 | 744 | }), |
| 766 | 745 | ), |
| 767 | 746 | Layer.provide( |
| 768 | − Layer.mock(ServerRuntimeStartup)({ | |
| 747 | + Layer.mock(ServerRuntimeStartup.ServerRuntimeStartup)({ | |
| 769 | 748 | awaitCommandReady: Effect.void, |
| 770 | 749 | markHttpListening: Effect.void, |
| 771 | 750 | enqueueCommand: (effect) => effect, |
| @@ -773,22 +752,22 @@ const buildAppUnderTest = (options?: { · 1 unmodified line | ||
| 773 | 752 | }), |
| 774 | 753 | ), |
| 775 | 754 | Layer.provide( |
| 776 | − Layer.mock(ServerEnvironment)({ | |
| 755 | + Layer.mock(ServerEnvironment.ServerEnvironment)({ | |
| 777 | 756 | getEnvironmentId: Effect.succeed(testEnvironmentDescriptor.environmentId), |
| 778 | 757 | getDescriptor: Effect.succeed(testEnvironmentDescriptor), |
| 779 | 758 | ...options?.layers?.serverEnvironment, |
| 780 | 759 | }), |
| 781 | 760 | ), |
| 782 | 761 | Layer.provide( |
| 783 | − Layer.mock(RepositoryIdentityResolver)({ | |
| 762 | + Layer.mock(RepositoryIdentityResolver.RepositoryIdentityResolver)({ | |
| 784 | 763 | resolve: () => Effect.succeed(null), |
| 785 | 764 | ...options?.layers?.repositoryIdentityResolver, |
| 786 | 765 | }), |
| 787 | 766 | ), |
| 788 | 767 | Layer.provide( |
| 789 | 768 | Layer.succeed( |
| 790 | − CloudManagedEndpointRuntime, | |
| 791 | − CloudManagedEndpointRuntime.of({ | |
| 769 | + CloudManagedEndpointRuntime.CloudManagedEndpointRuntime, | |
| 770 | + CloudManagedEndpointRuntime.CloudManagedEndpointRuntime.of({ | |
| 792 | 771 | applyConfig: () => Effect.succeed({ status: "disabled" }), |
| 793 | 772 | ...options?.layers?.cloudManagedEndpointRuntime, |
| 794 | 773 | }), |
| @@ -5939,14 +5918,14 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 5144 unmodified lines | ||
| 5939 | 5918 | }), |
| 5940 | 5919 | ); |
| 5941 | 5920 | const fetchRemote = vi.fn( |
| 5942 | − (_: Parameters<GitVcsDriver.GitVcsDriverShape["fetchRemote"]>[0]) => | |
| 5921 | + (_: Parameters<GitVcsDriver.GitVcsDriver["Service"]["fetchRemote"]>[0]) => | |
| 5943 | 5922 | Effect.sync(() => { |
| 5944 | 5923 | bootstrapGitOperations.push("fetch"); |
| 5945 | 5924 | }), |
| 5946 | 5925 | ); |
| 5947 | 5926 | const fetchedOriginCommit = "0123456789abcdef0123456789abcdef01234567"; |
| 5948 | 5927 | const resolveRemoteTrackingCommit = vi.fn( |
| 5949 | − (_: Parameters<GitVcsDriver.GitVcsDriverShape["resolveRemoteTrackingCommit"]>[0]) => | |
| 5928 | + (_: Parameters<GitVcsDriver.GitVcsDriver["Service"]["resolveRemoteTrackingCommit"]>[0]) => | |
| 5950 | 5929 | Effect.sync(() => { |
| 5951 | 5930 | bootstrapGitOperations.push("resolve-remote-commit"); |
| 5952 | 5931 | return { |
| @@ -5956,7 +5935,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 3 unmodified lines | ||
| 5956 | 5935 | }), |
| 5957 | 5936 | ); |
| 5958 | 5937 | const createWorktree = vi.fn( |
| 5959 | − (_: Parameters<GitVcsDriver.GitVcsDriverShape["createWorktree"]>[0]) => | |
| 5938 | + (_: Parameters<GitVcsDriver.GitVcsDriver["Service"]["createWorktree"]>[0]) => | |
| 5960 | 5939 | Effect.sync(() => { |
| 5961 | 5940 | bootstrapGitOperations.push("create-worktree"); |
| 5962 | 5941 | return { |
| @@ -5968,7 +5947,11 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 5 unmodified lines | ||
| 5968 | 5947 | }), |
| 5969 | 5948 | ); |
| 5970 | 5949 | const runForThread = vi.fn( |
| 5971 | − (_: Parameters<ProjectSetupScriptRunnerShape["runForThread"]>[0]) => | |
| 5950 | + ( | |
| 5951 | + _: Parameters< | |
| 5952 | + ProjectSetupScriptRunner.ProjectSetupScriptRunner["Service"]["runForThread"] | |
| 5953 | + >[0], | |
| 5954 | + ) => | |
| 5972 | 5955 | Effect.succeed({ |
| 5973 | 5956 | status: "started" as const, |
| 5974 | 5957 | scriptId: "setup", |
| @@ -6102,7 +6085,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 127 unmodified lines | ||
| 6102 | 6085 | Effect.gen(function* () { |
| 6103 | 6086 | const dispatchedCommands: Array<OrchestrationCommand> = []; |
| 6104 | 6087 | const createWorktree = vi.fn( |
| 6105 | − (_: Parameters<GitVcsDriver.GitVcsDriverShape["createWorktree"]>[0]) => | |
| 6088 | + (_: Parameters<GitVcsDriver.GitVcsDriver["Service"]["createWorktree"]>[0]) => | |
| 6106 | 6089 | Effect.succeed({ |
| 6107 | 6090 | worktree: { |
| 6108 | 6091 | refName: "t3code/bootstrap-refName", |
| @@ -6111,8 +6094,16 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 2 unmodified lines | ||
| 6111 | 6094 | }), |
| 6112 | 6095 | ); |
| 6113 | 6096 | const runForThread = vi.fn( |
| 6114 | − (_: Parameters<ProjectSetupScriptRunnerShape["runForThread"]>[0]) => | |
| 6115 | − Effect.fail(new ProjectSetupScriptRunnerError({ message: "pty unavailable" })), | |
| 6097 | + ( | |
| 6098 | + _: Parameters< | |
| 6099 | + ProjectSetupScriptRunner.ProjectSetupScriptRunner["Service"]["runForThread"] | |
| 6100 | + >[0], | |
| 6101 | + ) => | |
| 6102 | + Effect.fail( | |
| 6103 | + new ProjectSetupScriptRunner.ProjectSetupScriptRunnerError({ | |
| 6104 | + message: "pty unavailable", | |
| 6105 | + }), | |
| 6106 | + ), | |
| 6116 | 6107 | ); |
| 6117 | 6108 | |
| 6118 | 6109 | yield* buildAppUnderTest({ |
| @@ -6196,7 +6187,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 77 unmodified lines | ||
| 6196 | 6187 | Effect.gen(function* () { |
| 6197 | 6188 | const dispatchedCommands: Array<OrchestrationCommand> = []; |
| 6198 | 6189 | const createWorktree = vi.fn( |
| 6199 | − (_: Parameters<GitVcsDriver.GitVcsDriverShape["createWorktree"]>[0]) => | |
| 6190 | + (_: Parameters<GitVcsDriver.GitVcsDriver["Service"]["createWorktree"]>[0]) => | |
| 6200 | 6191 | Effect.succeed({ |
| 6201 | 6192 | worktree: { |
| 6202 | 6193 | refName: "t3code/bootstrap-refName", |
| @@ -6205,7 +6196,11 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 2 unmodified lines | ||
| 6205 | 6196 | }), |
| 6206 | 6197 | ); |
| 6207 | 6198 | const runForThread = vi.fn( |
| 6208 | − (_: Parameters<ProjectSetupScriptRunnerShape["runForThread"]>[0]) => | |
| 6199 | + ( | |
| 6200 | + _: Parameters< | |
| 6201 | + ProjectSetupScriptRunner.ProjectSetupScriptRunner["Service"]["runForThread"] | |
| 6202 | + >[0], | |
| 6203 | + ) => | |
| 6209 | 6204 | Effect.succeed({ |
| 6210 | 6205 | status: "started" as const, |
| 6211 | 6206 | scriptId: "setup", |
| @@ -6315,7 +6310,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 103 unmodified lines | ||
| 6315 | 6310 | Effect.gen(function* () { |
| 6316 | 6311 | const dispatchedCommands: Array<OrchestrationCommand> = []; |
| 6317 | 6312 | const createWorktree = vi.fn( |
| 6318 | − (_: Parameters<GitVcsDriver.GitVcsDriverShape["createWorktree"]>[0]) => | |
| 6313 | + (_: Parameters<GitVcsDriver.GitVcsDriver["Service"]["createWorktree"]>[0]) => | |
| 6319 | 6314 | Effect.die(new Error("worktree exploded")), |
| 6320 | 6315 | ); |
| 6321 | 6316 | |
| @@ -4,7 +4,7 @@ import * as Layer from "effect/Layer"; · 3 unmodified lines | ||
| 4 | 4 | import { FetchHttpClient, HttpRouter, HttpServer } from "effect/unstable/http"; |
| 5 | 5 | import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder"; |
| 6 | 6 | |
| 7 | −import { ServerConfig } from "./config.ts"; | |
| 7 | +import * as ServerConfig from "./config.ts"; | |
| 8 | 8 | import { |
| 9 | 9 | otlpTracesProxyRouteLayer, |
| 10 | 10 | assetRouteLayer, |
| @@ -16,15 +16,15 @@ import { fixPath } from "./os-jank.ts"; · 5 unmodified lines | ||
| 16 | 16 | import { websocketRpcRouteLayer } from "./ws.ts"; |
| 17 | 17 | import * as ExternalLauncher from "./process/externalLauncher.ts"; |
| 18 | 18 | import { layerConfig as SqlitePersistenceLayerLive } from "./persistence/Layers/Sqlite.ts"; |
| 19 | −import { ServerLifecycleEventsLive } from "./serverLifecycleEvents.ts"; | |
| 19 | +import * as ServerLifecycleEvents from "./serverLifecycleEvents.ts"; | |
| 20 | 20 | import { AnalyticsServiceLayerLive } from "./telemetry/Layers/AnalyticsService.ts"; |
| 21 | 21 | import { ProviderSessionDirectoryLive } from "./provider/Layers/ProviderSessionDirectory.ts"; |
| 22 | 22 | import { ProviderSessionRuntimeRepositoryLive } from "./persistence/Layers/ProviderSessionRuntime.ts"; |
| 23 | 23 | import { ProviderAdapterRegistryLive } from "./provider/Layers/ProviderAdapterRegistry.ts"; |
| 24 | −import { ProviderEventLoggersLive } from "./provider/Layers/ProviderEventLoggers.ts"; | |
| 24 | +import * as ProviderEventLoggers from "./provider/Layers/ProviderEventLoggers.ts"; | |
| 25 | 25 | import { ProviderServiceLive } from "./provider/Layers/ProviderService.ts"; |
| 26 | 26 | import { ProviderSessionReaperLive } from "./provider/Layers/ProviderSessionReaper.ts"; |
| 27 | −import { OpenCodeRuntimeLive } from "./provider/opencodeRuntime.ts"; | |
| 27 | +import * as OpenCodeRuntime from "./provider/opencodeRuntime.ts"; | |
| 28 | 28 | import { CheckpointDiffQueryLive } from "./checkpointing/Layers/CheckpointDiffQuery.ts"; |
| 29 | 29 | import { CheckpointStoreLive } from "./checkpointing/Layers/CheckpointStore.ts"; |
| 30 | 30 | import * as AzureDevOpsCli from "./sourceControl/AzureDevOpsCli.ts"; |
| @@ -40,8 +40,8 @@ import * as PreviewManager from "./preview/Manager.ts"; · 9 unmodified lines | ||
| 40 | 40 | import * as PortScanner from "./preview/PortScanner.ts"; |
| 41 | 41 | import * as ProcessRunner from "./processRunner.ts"; |
| 42 | 42 | import * as GitManager from "./git/GitManager.ts"; |
| 43 | −import { KeybindingsLive } from "./keybindings.ts"; | |
| 44 | −import { ServerRuntimeStartup, ServerRuntimeStartupLive } from "./serverRuntimeStartup.ts"; | |
| 43 | +import * as Keybindings from "./keybindings.ts"; | |
| 44 | +import * as ServerRuntimeStartup from "./serverRuntimeStartup.ts"; | |
| 45 | 45 | import { OrchestrationReactorLive } from "./orchestration/Layers/OrchestrationReactor.ts"; |
| 46 | 46 | import { RuntimeReceiptBusLive } from "./orchestration/Layers/RuntimeReceiptBus.ts"; |
| 47 | 47 | import { ProviderRuntimeIngestionLive } from "./orchestration/Layers/ProviderRuntimeIngestion.ts"; |
| @@ -51,7 +51,7 @@ import { ThreadDeletionReactorLive } from "./orchestration/Layers/ThreadDeletion · 3 unmodified lines | ||
| 51 | 51 | import * as AgentAwarenessRelay from "./relay/AgentAwarenessRelay.ts"; |
| 52 | 52 | import { hasCloudPublicConfig } from "./cloud/publicConfig.ts"; |
| 53 | 53 | import { ProviderRegistryLive } from "./provider/Layers/ProviderRegistry.ts"; |
| 54 | −import { ServerSettingsLive } from "./serverSettings.ts"; | |
| 54 | +import * as ServerSettings from "./serverSettings.ts"; | |
| 55 | 55 | import { ProjectFaviconResolverLive } from "./project/Layers/ProjectFaviconResolver.ts"; |
| 56 | 56 | import { RepositoryIdentityResolverLive } from "./project/Layers/RepositoryIdentityResolver.ts"; |
| 57 | 57 | import * as WorkspaceEntries from "./workspace/WorkspaceEntries.ts"; |
| @@ -112,14 +112,14 @@ const PtyAdapterLive = Layer.unwrap( · 54 unmodified lines | ||
| 112 | 112 | |
| 113 | 113 | const RelayClientLive = Layer.unwrap( |
| 114 | 114 | Effect.gen(function* () { |
| 115 | − const config = yield* ServerConfig; | |
| 115 | + const config = yield* ServerConfig.ServerConfig; | |
| 116 | 116 | return RelayClient.layerCloudflared({ baseDir: config.baseDir }); |
| 117 | 117 | }), |
| 118 | 118 | ); |
| 119 | 119 | |
| 120 | 120 | const HttpServerLive = Layer.unwrap( |
| 121 | 121 | Effect.gen(function* () { |
| 122 | − const config = yield* ServerConfig; | |
| 122 | + const config = yield* ServerConfig.ServerConfig; | |
| 123 | 123 | if (typeof Bun !== "undefined") { |
| 124 | 124 | const BunHttpServer = yield* Effect.promise( |
| 125 | 125 | () => import("@effect/platform-bun/BunHttpServer"), |
| @@ -292,7 +292,7 @@ const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe( · 166 unmodified lines | ||
| 292 | 292 | Layer.provideMerge(ProviderRuntimeLayerLive), |
| 293 | 293 | Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)), |
| 294 | 294 | Layer.provideMerge(PersistenceLayerLive), |
| 295 | − Layer.provideMerge(KeybindingsLive), | |
| 295 | + Layer.provideMerge(Keybindings.layer), | |
| 296 | 296 | Layer.provideMerge(ProviderRegistryLive), |
| 297 | 297 | // The instance registry is the new routing keystone — text generation, |
| 298 | 298 | // adapter lookup, and runtime ingestion all resolve `ProviderInstanceId` |
| @@ -305,14 +305,14 @@ const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe( · 6 unmodified lines | ||
| 305 | 305 | // `ProviderService` (canonical stream, written after event normalization). |
| 306 | 306 | // Provided once at the runtime level so every consumer sees the same |
| 307 | 307 | // logger instances. |
| 308 | − Layer.provideMerge(ProviderEventLoggersLive), | |
| 308 | + Layer.provideMerge(ProviderEventLoggers.ProviderEventLoggersLive), | |
| 309 | 309 | // `OpenCodeDriver.create()` yields `OpenCodeRuntime`; previously the old |
| 310 | 310 | // `ProviderRegistryLive` pulled `OpenCodeRuntimeLive` in for itself, but |
| 311 | 311 | // the rewritten registry reads snapshots off the instance registry and |
| 312 | 312 | // no longer transitively provides it. Exposing it at the runtime level |
| 313 | 313 | // keeps a single Live for all opencode consumers. |
| 314 | − Layer.provideMerge(OpenCodeRuntimeLive), | |
| 315 | − Layer.provideMerge(ServerSettingsLive), | |
| 314 | + Layer.provideMerge(OpenCodeRuntime.OpenCodeRuntimeLive), | |
| 315 | + Layer.provideMerge(ServerSettings.layer.pipe(Layer.provide(ServerSecretStore.layer))), | |
| 316 | 316 | Layer.provideMerge(WorkspaceLayerLive), |
| 317 | 317 | Layer.provideMerge(ProjectFaviconResolverLayerLive), |
| 318 | 318 | Layer.provideMerge(RepositoryIdentityResolverLive), |
| @@ -334,11 +334,11 @@ const RuntimeDependenciesLive = RuntimeCoreDependenciesLive.pipe( · 15 unmodified lines | ||
| 334 | 334 | Layer.provideMerge(TraceDiagnostics.layer), |
| 335 | 335 | Layer.provideMerge(AnalyticsServiceLayerLive), |
| 336 | 336 | Layer.provideMerge(ExternalLauncher.layer), |
| 337 | − Layer.provideMerge(ServerLifecycleEventsLive), | |
| 337 | + Layer.provideMerge(ServerLifecycleEvents.layer), | |
| 338 | 338 | Layer.provide(NetService.layer), |
| 339 | 339 | ); |
| 340 | 340 | |
| 341 | −const RuntimeServicesLive = ServerRuntimeStartupLive.pipe( | |
| 341 | +const RuntimeServicesLive = ServerRuntimeStartup.layer.pipe( | |
| 342 | 342 | Layer.provideMerge(RuntimeDependenciesLive), |
| 343 | 343 | ); |
| 344 | 344 | |
| @@ -361,14 +361,14 @@ export const makeRoutesLayer = Layer.mergeAll( · 16 unmodified lines | ||
| 361 | 361 | |
| 362 | 362 | export const makeServerLayer = Layer.unwrap( |
| 363 | 363 | Effect.gen(function* () { |
| 364 | − const config = yield* ServerConfig; | |
| 364 | + const config = yield* ServerConfig.ServerConfig; | |
| 365 | 365 | |
| 366 | 366 | yield* fixPath(); |
| 367 | 367 | |
| 368 | 368 | const httpListeningLayer = Layer.effectDiscard( |
| 369 | 369 | Effect.gen(function* () { |
| 370 | 370 | yield* HttpServer.HttpServer; |
| 371 | − const startup = yield* ServerRuntimeStartup; | |
| 371 | + const startup = yield* ServerRuntimeStartup.ServerRuntimeStartup; | |
| 372 | 372 | yield* startup.markHttpListening; |
| 373 | 373 | }), |
| 374 | 374 | ); |
| @@ -4,13 +4,13 @@ import { assertTrue } from "@effect/vitest/utils"; · 3 unmodified lines | ||
| 4 | 4 | import * as Effect from "effect/Effect"; |
| 5 | 5 | import * as Option from "effect/Option"; |
| 6 | 6 | |
| 7 | −import { ServerLifecycleEvents, ServerLifecycleEventsLive } from "./serverLifecycleEvents.ts"; | |
| 7 | +import * as ServerLifecycleEvents from "./serverLifecycleEvents.ts"; | |
| 8 | 8 | |
| 9 | 9 | it.effect( |
| 10 | 10 | "publishes lifecycle events without subscribers and snapshots the latest welcome/ready", |
| 11 | 11 | () => |
| 12 | 12 | Effect.gen(function* () { |
| 13 | − const lifecycleEvents = yield* ServerLifecycleEvents; | |
| 13 | + const lifecycleEvents = yield* ServerLifecycleEvents.ServerLifecycleEvents; | |
| 14 | 14 | const environment = { |
| 15 | 15 | environmentId: EnvironmentId.make("environment-test"), |
| 16 | 16 | label: "Test environment", |
| @@ -49,5 +49,5 @@ it.effect( · 32 unmodified lines | ||
| 49 | 49 | const snapshot = yield* lifecycleEvents.snapshot; |
| 50 | 50 | assert.equal(snapshot.sequence, 2); |
| 51 | 51 | assert.deepEqual(snapshot.events.map((event) => event.type).toSorted(), ["ready", "welcome"]); |
| 52 | − }).pipe(Effect.provide(ServerLifecycleEventsLive)), | |
| 52 | + }).pipe(Effect.provide(ServerLifecycleEvents.layer)), | |
| 53 | 53 | ); |
| @@ -1,9 +1,9 @@ | ||
| 1 | 1 | import type { ServerLifecycleStreamEvent } from "@t3tools/contracts"; |
| 2 | +import * as Context from "effect/Context"; | |
| 2 | 3 | import * as Effect from "effect/Effect"; |
| 3 | 4 | import * as Layer from "effect/Layer"; |
| 4 | 5 | import * as PubSub from "effect/PubSub"; |
| 5 | 6 | import * as Ref from "effect/Ref"; |
| 6 | −import * as Context from "effect/Context"; | |
| 7 | 7 | import * as Stream from "effect/Stream"; |
| 8 | 8 | |
| 9 | 9 | type LifecycleEventInput = |
| @@ -15,44 +15,41 @@ interface SnapshotState { · 5 unmodified lines | ||
| 15 | 15 | readonly events: ReadonlyArray<ServerLifecycleStreamEvent>; |
| 16 | 16 | } |
| 17 | 17 | |
| 18 | −export interface ServerLifecycleEventsShape { | |
| 19 | − readonly publish: (event: LifecycleEventInput) => Effect.Effect<ServerLifecycleStreamEvent>; | |
| 20 | − readonly snapshot: Effect.Effect<SnapshotState>; | |
| 21 | − readonly stream: Stream.Stream<ServerLifecycleStreamEvent>; | |
| 22 | −} | |
| 23 | − | |
| 24 | 18 | export class ServerLifecycleEvents extends Context.Service< |
| 25 | 19 | ServerLifecycleEvents, |
| 26 | − ServerLifecycleEventsShape | |
| 20 | + { | |
| 21 | + readonly publish: (event: LifecycleEventInput) => Effect.Effect<ServerLifecycleStreamEvent>; | |
| 22 | + readonly snapshot: Effect.Effect<SnapshotState>; | |
| 23 | + readonly stream: Stream.Stream<ServerLifecycleStreamEvent>; | |
| 24 | + } | |
| 27 | 25 | >()("t3/serverLifecycleEvents") {} |
| 28 | 26 | |
| 29 | −export const ServerLifecycleEventsLive = Layer.effect( | |
| 30 | − ServerLifecycleEvents, | |
| 31 | − Effect.gen(function* () { | |
| 32 | − const pubsub = yield* PubSub.unbounded<ServerLifecycleStreamEvent>(); | |
| 33 | − const state = yield* Ref.make<SnapshotState>({ | |
| 34 | − sequence: 0, | |
| 35 | − events: [], | |
| 36 | − }); | |
| 27 | +const make = Effect.gen(function* () { | |
| 28 | + const pubsub = yield* PubSub.unbounded<ServerLifecycleStreamEvent>(); | |
| 29 | + const state = yield* Ref.make<SnapshotState>({ | |
| 30 | + sequence: 0, | |
| 31 | + events: [], | |
| 32 | + }); | |
| 33 | + | |
| 34 | + return { | |
| 35 | + publish: (event) => | |
| 36 | + Ref.modify(state, (current) => { | |
| 37 | + const nextSequence = current.sequence + 1; | |
| 38 | + const nextEvent = { | |
| 39 | + ...event, | |
| 40 | + sequence: nextSequence, | |
| 41 | + } satisfies ServerLifecycleStreamEvent; | |
| 42 | + const nextEvents = | |
| 43 | + nextEvent.type === "welcome" | |
| 44 | + ? [nextEvent, ...current.events.filter((entry) => entry.type !== "welcome")] | |
| 45 | + : [nextEvent, ...current.events.filter((entry) => entry.type !== "ready")]; | |
| 46 | + return [nextEvent, { sequence: nextSequence, events: nextEvents }] as const; | |
| 47 | + }).pipe(Effect.tap((event) => PubSub.publish(pubsub, event))), | |
| 48 | + snapshot: Ref.get(state), | |
| 49 | + get stream() { | |
| 50 | + return Stream.fromPubSub(pubsub); | |
| 51 | + }, | |
| 52 | + } satisfies ServerLifecycleEvents["Service"]; | |
| 53 | +}); | |
| 37 | 54 | |
| 38 | − return { | |
| 39 | − publish: (event) => | |
| 40 | − Ref.modify(state, (current) => { | |
| 41 | − const nextSequence = current.sequence + 1; | |
| 42 | − const nextEvent = { | |
| 43 | − ...event, | |
| 44 | − sequence: nextSequence, | |
| 45 | − } satisfies ServerLifecycleStreamEvent; | |
| 46 | − const nextEvents = | |
| 47 | − nextEvent.type === "welcome" | |
| 48 | − ? [nextEvent, ...current.events.filter((entry) => entry.type !== "welcome")] | |
| 49 | − : [nextEvent, ...current.events.filter((entry) => entry.type !== "ready")]; | |
| 50 | − return [nextEvent, { sequence: nextSequence, events: nextEvents }] as const; | |
| 51 | − }).pipe(Effect.tap((event) => PubSub.publish(pubsub, event))), | |
| 52 | − snapshot: Ref.get(state), | |
| 53 | − get stream() { | |
| 54 | − return Stream.fromPubSub(pubsub); | |
| 55 | − }, | |
| 56 | − } satisfies ServerLifecycleEventsShape; | |
| 57 | − }), | |
| 58 | −); | |
| 55 | +export const layer = Layer.effect(ServerLifecycleEvents, make); | |
| @@ -10,24 +10,14 @@ import * as PlatformError from "effect/PlatformError"; · 9 unmodified lines | ||
| 10 | 10 | import * as Ref from "effect/Ref"; |
| 11 | 11 | import * as Stream from "effect/Stream"; |
| 12 | 12 | |
| 13 | −import { ServerConfig } from "./config.ts"; | |
| 14 | −import { | |
| 15 | − OrchestrationEngineService, | |
| 16 | − type OrchestrationEngineShape, | |
| 17 | −} from "./orchestration/Services/OrchestrationEngine.ts"; | |
| 18 | −import { ProjectionSnapshotQuery } from "./orchestration/Services/ProjectionSnapshotQuery.ts"; | |
| 19 | −import { AnalyticsService } from "./telemetry/Services/AnalyticsService.ts"; | |
| 20 | −import { | |
| 21 | − getAutoBootstrapDefaultModelSelection, | |
| 22 | − launchStartupHeartbeat, | |
| 23 | − makeCommandGate, | |
| 24 | − resolveAutoBootstrapWelcomeTargets, | |
| 25 | − resolveWelcomeBase, | |
| 26 | − ServerRuntimeStartupError, | |
| 27 | −} from "./serverRuntimeStartup.ts"; | |
| 13 | +import * as ServerConfig from "./config.ts"; | |
| 14 | +import * as OrchestrationEngine from "./orchestration/Services/OrchestrationEngine.ts"; | |
| 15 | +import * as ProjectionSnapshotQuery from "./orchestration/Services/ProjectionSnapshotQuery.ts"; | |
| 16 | +import * as AnalyticsService from "./telemetry/Services/AnalyticsService.ts"; | |
| 17 | +import * as ServerRuntimeStartup from "./serverRuntimeStartup.ts"; | |
| 28 | 18 | |
| 29 | 19 | it("uses the canonical Codex default for auto-bootstrapped model selection", () => { |
| 30 | − assert.deepStrictEqual(getAutoBootstrapDefaultModelSelection(), { | |
| 20 | + assert.deepStrictEqual(ServerRuntimeStartup.getAutoBootstrapDefaultModelSelection(), { | |
| 31 | 21 | instanceId: ProviderInstanceId.make("codex"), |
| 32 | 22 | model: DEFAULT_MODEL, |
| 33 | 23 | }); |
| @@ -37,7 +27,7 @@ it.effect("enqueueCommand waits for readiness and then drains queued work", () = · 3 unmodified lines | ||
| 37 | 27 | Effect.scoped( |
| 38 | 28 | Effect.gen(function* () { |
| 39 | 29 | const executionCount = yield* Ref.make(0); |
| 40 | − const commandGate = yield* makeCommandGate; | |
| 30 | + const commandGate = yield* ServerRuntimeStartup.makeCommandGate; | |
| 41 | 31 | |
| 42 | 32 | const queuedCommandFiber = yield* commandGate |
| 43 | 33 | .enqueueCommand(Ref.updateAndGet(executionCount, (count) => count + 1)) |
| @@ -58,7 +48,7 @@ it.effect("enqueueCommand waits for readiness and then drains queued work", () = · 14 unmodified lines | ||
| 58 | 48 | it.effect("enqueueCommand fails queued work when readiness fails", () => |
| 59 | 49 | Effect.scoped( |
| 60 | 50 | Effect.gen(function* () { |
| 61 | − const commandGate = yield* makeCommandGate; | |
| 51 | + const commandGate = yield* ServerRuntimeStartup.makeCommandGate; | |
| 62 | 52 | const failure = yield* Deferred.make<void, never>(); |
| 63 | 53 | |
| 64 | 54 | const queuedCommandFiber = yield* commandGate |
| @@ -66,13 +56,13 @@ it.effect("enqueueCommand fails queued work when readiness fails", () => · 1 unmodified line | ||
| 66 | 56 | .pipe(Effect.forkScoped); |
| 67 | 57 | |
| 68 | 58 | yield* commandGate.failCommandReady( |
| 69 | − new ServerRuntimeStartupError({ | |
| 70 | − message: "startup failed", | |
| 59 | + new ServerRuntimeStartup.ServerRuntimeStartupError({ | |
| 60 | + stage: "command-readiness", | |
| 71 | 61 | }), |
| 72 | 62 | ); |
| 73 | 63 | |
| 74 | 64 | const error = yield* Effect.flip(Fiber.join(queuedCommandFiber)); |
| 75 | − assert.equal(error.message, "startup failed"); | |
| 65 | + assert.equal(error.message, "Server runtime startup failed before command readiness."); | |
| 76 | 66 | }), |
| 77 | 67 | ), |
| 78 | 68 | ); |
| @@ -82,8 +72,8 @@ it.effect("launchStartupHeartbeat does not block the caller while counts are loa · 3 unmodified lines | ||
| 82 | 72 | Effect.gen(function* () { |
| 83 | 73 | const releaseCounts = yield* Deferred.make<void, never>(); |
| 84 | 74 | |
| 85 | − yield* launchStartupHeartbeat.pipe( | |
| 86 | − Effect.provideService(ProjectionSnapshotQuery, { | |
| 75 | + yield* ServerRuntimeStartup.launchStartupHeartbeat.pipe( | |
| 76 | + Effect.provideService(ProjectionSnapshotQuery.ProjectionSnapshotQuery, { | |
| 87 | 77 | getCommandReadModel: () => Effect.die("unused"), |
| 88 | 78 | getSnapshot: () => Effect.die("unused"), |
| 89 | 79 | getShellSnapshot: () => Effect.die("unused"), |
| @@ -104,7 +94,7 @@ it.effect("launchStartupHeartbeat does not block the caller while counts are loa · 14 unmodified lines | ||
| 104 | 94 | getThreadShellById: () => Effect.succeed(Option.none()), |
| 105 | 95 | getThreadDetailById: () => Effect.succeed(Option.none()), |
| 106 | 96 | }), |
| 107 | − Effect.provideService(AnalyticsService, { | |
| 97 | + Effect.provideService(AnalyticsService.AnalyticsService, { | |
| 108 | 98 | record: () => Effect.void, |
| 109 | 99 | flush: Effect.void, |
| 110 | 100 | }), |
| @@ -115,8 +105,8 @@ it.effect("launchStartupHeartbeat does not block the caller while counts are loa · 4 unmodified lines | ||
| 115 | 105 | |
| 116 | 106 | it.effect("resolveWelcomeBase derives cwd and project name from server config", () => |
| 117 | 107 | Effect.gen(function* () { |
| 118 | − const welcome = yield* resolveWelcomeBase.pipe( | |
| 119 | − Effect.provideService(ServerConfig, { | |
| 108 | + const welcome = yield* ServerRuntimeStartup.resolveWelcomeBase.pipe( | |
| 109 | + Effect.provideService(ServerConfig.ServerConfig, { | |
| 120 | 110 | cwd: "/tmp/startup-project", |
| 121 | 111 | } as never), |
| 122 | 112 | ); |
| @@ -134,12 +124,12 @@ it.effect("resolveAutoBootstrapWelcomeTargets returns existing project and threa · 11 unmodified lines | ||
| 134 | 124 | |
| 135 | 125 | return Effect.gen(function* () { |
| 136 | 126 | const dispatchCalls = yield* Ref.make<ReadonlyArray<string>>([]); |
| 137 | − const targets = yield* resolveAutoBootstrapWelcomeTargets.pipe( | |
| 138 | − Effect.provideService(ServerConfig, { | |
| 127 | + const targets = yield* ServerRuntimeStartup.resolveAutoBootstrapWelcomeTargets.pipe( | |
| 128 | + Effect.provideService(ServerConfig.ServerConfig, { | |
| 139 | 129 | cwd: "/tmp/startup-project", |
| 140 | 130 | autoBootstrapProjectFromCwd: true, |
| 141 | 131 | } as never), |
| 142 | − Effect.provideService(ProjectionSnapshotQuery, { | |
| 132 | + Effect.provideService(ProjectionSnapshotQuery.ProjectionSnapshotQuery, { | |
| 143 | 133 | getCommandReadModel: () => Effect.die("unused"), |
| 144 | 134 | getSnapshot: () => Effect.die("unused"), |
| 145 | 135 | getShellSnapshot: () => Effect.die("unused"), |
| @@ -152,7 +142,7 @@ it.effect("resolveAutoBootstrapWelcomeTargets returns existing project and threa · 6 unmodified lines | ||
| 152 | 142 | id: bootstrapProjectId, |
| 153 | 143 | title: "Startup Project", |
| 154 | 144 | workspaceRoot: "/tmp/startup-project", |
| 155 | − defaultModelSelection: getAutoBootstrapDefaultModelSelection(), | |
| 145 | + defaultModelSelection: ServerRuntimeStartup.getAutoBootstrapDefaultModelSelection(), | |
| 156 | 146 | scripts: [], |
| 157 | 147 | createdAt: "2026-01-01T00:00:00.000Z", |
| 158 | 148 | updatedAt: "2026-01-01T00:00:00.000Z", |
| @@ -166,14 +156,14 @@ it.effect("resolveAutoBootstrapWelcomeTargets returns existing project and threa · 7 unmodified lines | ||
| 166 | 156 | getThreadShellById: () => Effect.die("unused"), |
| 167 | 157 | getThreadDetailById: () => Effect.die("unused"), |
| 168 | 158 | }), |
| 169 | − Effect.provideService(OrchestrationEngineService, { | |
| 159 | + Effect.provideService(OrchestrationEngine.OrchestrationEngineService, { | |
| 170 | 160 | readEvents: () => Stream.empty, |
| 171 | 161 | dispatch: (command) => |
| 172 | 162 | Ref.update(dispatchCalls, (calls) => [...calls, command.type]).pipe( |
| 173 | 163 | Effect.as({ sequence: 1 }), |
| 174 | 164 | ), |
| 175 | 165 | streamDomainEvents: Stream.empty, |
| 176 | − } satisfies OrchestrationEngineShape), | |
| 166 | + } satisfies OrchestrationEngine.OrchestrationEngineService["Service"]), | |
| 177 | 167 | Effect.provide(NodeServices.layer), |
| 178 | 168 | ); |
| 179 | 169 | |
| @@ -188,12 +178,12 @@ it.effect("resolveAutoBootstrapWelcomeTargets returns existing project and threa · 8 unmodified lines | ||
| 188 | 178 | it.effect("resolveAutoBootstrapWelcomeTargets creates a project and thread when missing", () => |
| 189 | 179 | Effect.gen(function* () { |
| 190 | 180 | const dispatchCalls = yield* Ref.make<ReadonlyArray<string>>([]); |
| 191 | − const targets = yield* resolveAutoBootstrapWelcomeTargets.pipe( | |
| 192 | − Effect.provideService(ServerConfig, { | |
| 181 | + const targets = yield* ServerRuntimeStartup.resolveAutoBootstrapWelcomeTargets.pipe( | |
| 182 | + Effect.provideService(ServerConfig.ServerConfig, { | |
| 193 | 183 | cwd: "/tmp/startup-project", |
| 194 | 184 | autoBootstrapProjectFromCwd: true, |
| 195 | 185 | } as never), |
| 196 | − Effect.provideService(ProjectionSnapshotQuery, { | |
| 186 | + Effect.provideService(ProjectionSnapshotQuery.ProjectionSnapshotQuery, { | |
| 197 | 187 | getCommandReadModel: () => Effect.die("unused"), |
| 198 | 188 | getSnapshot: () => Effect.die("unused"), |
| 199 | 189 | getShellSnapshot: () => Effect.die("unused"), |
| @@ -208,14 +198,14 @@ it.effect("resolveAutoBootstrapWelcomeTargets creates a project and thread when · 8 unmodified lines | ||
| 208 | 198 | getThreadShellById: () => Effect.die("unused"), |
| 209 | 199 | getThreadDetailById: () => Effect.die("unused"), |
| 210 | 200 | }), |
| 211 | − Effect.provideService(OrchestrationEngineService, { | |
| 201 | + Effect.provideService(OrchestrationEngine.OrchestrationEngineService, { | |
| 212 | 202 | readEvents: () => Stream.empty, |
| 213 | 203 | dispatch: (command) => |
| 214 | 204 | Ref.update(dispatchCalls, (calls) => [...calls, command.type]).pipe( |
| 215 | 205 | Effect.as({ sequence: 1 }), |
| 216 | 206 | ), |
| 217 | 207 | streamDomainEvents: Stream.empty, |
| 218 | − } satisfies OrchestrationEngineShape), | |
| 208 | + } satisfies OrchestrationEngine.OrchestrationEngineService["Service"]), | |
| 219 | 209 | Effect.provide(NodeServices.layer), |
| 220 | 210 | ); |
| 221 | 211 | |
| @@ -236,12 +226,12 @@ it.effect("resolveAutoBootstrapWelcomeTargets preserves typed UUID generation fa · 14 unmodified lines | ||
| 236 | 226 | }); |
| 237 | 227 | const dispatchCalls = yield* Ref.make<ReadonlyArray<string>>([]); |
| 238 | 228 | |
| 239 | − const error = yield* resolveAutoBootstrapWelcomeTargets.pipe( | |
| 240 | − Effect.provideService(ServerConfig, { | |
| 229 | + const error = yield* ServerRuntimeStartup.resolveAutoBootstrapWelcomeTargets.pipe( | |
| 230 | + Effect.provideService(ServerConfig.ServerConfig, { | |
| 241 | 231 | cwd: "/tmp/startup-project", |
| 242 | 232 | autoBootstrapProjectFromCwd: true, |
| 243 | 233 | } as never), |
| 244 | − Effect.provideService(ProjectionSnapshotQuery, { | |
| 234 | + Effect.provideService(ProjectionSnapshotQuery.ProjectionSnapshotQuery, { | |
| 245 | 235 | getCommandReadModel: () => Effect.die("unused"), |
| 246 | 236 | getSnapshot: () => Effect.die("unused"), |
| 247 | 237 | getShellSnapshot: () => Effect.die("unused"), |
| @@ -256,14 +246,14 @@ it.effect("resolveAutoBootstrapWelcomeTargets preserves typed UUID generation fa · 8 unmodified lines | ||
| 256 | 246 | getThreadShellById: () => Effect.die("unused"), |
| 257 | 247 | getThreadDetailById: () => Effect.die("unused"), |
| 258 | 248 | }), |
| 259 | − Effect.provideService(OrchestrationEngineService, { | |
| 249 | + Effect.provideService(OrchestrationEngine.OrchestrationEngineService, { | |
| 260 | 250 | readEvents: () => Stream.empty, |
| 261 | 251 | dispatch: (command) => |
| 262 | 252 | Ref.update(dispatchCalls, (calls) => [...calls, command.type]).pipe( |
| 263 | 253 | Effect.as({ sequence: 1 }), |
| 264 | 254 | ), |
| 265 | 255 | streamDomainEvents: Stream.empty, |
| 266 | − } satisfies OrchestrationEngineShape), | |
| 256 | + } satisfies OrchestrationEngine.OrchestrationEngineService["Service"]), | |
| 267 | 257 | Effect.provideService(Crypto.Crypto, { |
| 268 | 258 | ...crypto, |
| 269 | 259 | randomUUIDv4: Effect.fail(uuidError), |
| @@ -7,8 +7,10 @@ import { · 6 unmodified lines | ||
| 7 | 7 | ProviderInstanceId, |
| 8 | 8 | ThreadId, |
| 9 | 9 | } from "@t3tools/contracts"; |
| 10 | −import * as Data from "effect/Data"; | |
| 10 | +import * as Console from "effect/Console"; | |
| 11 | +import * as Context from "effect/Context"; | |
| 11 | 12 | import * as Crypto from "effect/Crypto"; |
| 13 | +import * as DateTime from "effect/DateTime"; | |
| 12 | 14 | import * as Deferred from "effect/Deferred"; |
| 13 | 15 | import * as Effect from "effect/Effect"; |
| 14 | 16 | import * as Exit from "effect/Exit"; |
| @@ -17,23 +19,21 @@ import * as Option from "effect/Option"; · 2 unmodified lines | ||
| 17 | 19 | import * as Path from "effect/Path"; |
| 18 | 20 | import * as Queue from "effect/Queue"; |
| 19 | 21 | import * as Ref from "effect/Ref"; |
| 22 | +import * as Schema from "effect/Schema"; | |
| 20 | 23 | import * as Scope from "effect/Scope"; |
| 21 | −import * as Context from "effect/Context"; | |
| 22 | −import * as Console from "effect/Console"; | |
| 23 | −import * as DateTime from "effect/DateTime"; | |
| 24 | 24 | |
| 25 | −import { ServerConfig } from "./config.ts"; | |
| 26 | −import { Keybindings } from "./keybindings.ts"; | |
| 25 | +import * as ServerConfig from "./config.ts"; | |
| 26 | +import * as Keybindings from "./keybindings.ts"; | |
| 27 | 27 | import * as ExternalLauncher from "./process/externalLauncher.ts"; |
| 28 | −import { OrchestrationEngineService } from "./orchestration/Services/OrchestrationEngine.ts"; | |
| 29 | −import { ProjectionSnapshotQuery } from "./orchestration/Services/ProjectionSnapshotQuery.ts"; | |
| 30 | −import { OrchestrationReactor } from "./orchestration/Services/OrchestrationReactor.ts"; | |
| 31 | −import { ServerLifecycleEvents } from "./serverLifecycleEvents.ts"; | |
| 32 | −import { ServerSettingsService } from "./serverSettings.ts"; | |
| 33 | −import { ServerEnvironment } from "./environment/Services/ServerEnvironment.ts"; | |
| 34 | −import { AnalyticsService } from "./telemetry/Services/AnalyticsService.ts"; | |
| 28 | +import * as OrchestrationEngine from "./orchestration/Services/OrchestrationEngine.ts"; | |
| 29 | +import * as ProjectionSnapshotQuery from "./orchestration/Services/ProjectionSnapshotQuery.ts"; | |
| 30 | +import * as OrchestrationReactor from "./orchestration/Services/OrchestrationReactor.ts"; | |
| 31 | +import * as ServerLifecycleEvents from "./serverLifecycleEvents.ts"; | |
| 32 | +import * as ServerSettings from "./serverSettings.ts"; | |
| 33 | +import * as ServerEnvironment from "./environment/Services/ServerEnvironment.ts"; | |
| 34 | +import * as AnalyticsService from "./telemetry/Services/AnalyticsService.ts"; | |
| 35 | 35 | import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts"; |
| 36 | −import { ProviderSessionReaper } from "./provider/Services/ProviderSessionReaper.ts"; | |
| 36 | +import * as ProviderSessionReaper from "./provider/Services/ProviderSessionReaper.ts"; | |
| 37 | 37 | import { |
| 38 | 38 | formatHeadlessServeOutput, |
| 39 | 39 | formatHostForUrl, |
| @@ -41,22 +41,30 @@ import { · 1 unmodified line | ||
| 41 | 41 | issueHeadlessServeAccessInfo, |
| 42 | 42 | } from "./startupAccess.ts"; |
| 43 | 43 | |
| 44 | −export class ServerRuntimeStartupError extends Data.TaggedError("ServerRuntimeStartupError")<{ | |
| 45 | − readonly message: string; | |
| 46 | − readonly cause?: unknown; | |
| 47 | −}> {} | |
| 48 | − | |
| 49 | −export interface ServerRuntimeStartupShape { | |
| 50 | − readonly awaitCommandReady: Effect.Effect<void, ServerRuntimeStartupError>; | |
| 51 | − readonly markHttpListening: Effect.Effect<void>; | |
| 52 | − readonly enqueueCommand: <A, E>( | |
| 53 | − effect: Effect.Effect<A, E>, | |
| 54 | − ) => Effect.Effect<A, E | ServerRuntimeStartupError>; | |
| 44 | +export class ServerRuntimeStartupError extends Schema.TaggedErrorClass<ServerRuntimeStartupError>()( | |
| 45 | + "ServerRuntimeStartupError", | |
| 46 | + { | |
| 47 | + stage: Schema.Literal("command-readiness"), | |
| 48 | + cause: Schema.optional(Schema.Defect()), | |
| 49 | + }, | |
| 50 | +) { | |
| 51 | + override get message(): string { | |
| 52 | + switch (this.stage) { | |
| 53 | + case "command-readiness": | |
| 54 | + return "Server runtime startup failed before command readiness."; | |
| 55 | + } | |
| 56 | + } | |
| 55 | 57 | } |
| 56 | 58 | |
| 57 | 59 | export class ServerRuntimeStartup extends Context.Service< |
| 58 | 60 | ServerRuntimeStartup, |
| 59 | − ServerRuntimeStartupShape | |
| 61 | + { | |
| 62 | + readonly awaitCommandReady: Effect.Effect<void, ServerRuntimeStartupError>; | |
| 63 | + readonly markHttpListening: Effect.Effect<void>; | |
| 64 | + readonly enqueueCommand: <A, E>( | |
| 65 | + effect: Effect.Effect<A, E>, | |
| 66 | + ) => Effect.Effect<A, E | ServerRuntimeStartupError>; | |
| 67 | + } | |
| 60 | 68 | >()("t3/serverRuntimeStartup") {} |
| 61 | 69 | |
| 62 | 70 | interface QueuedCommand { |
| @@ -124,8 +132,8 @@ export const makeCommandGate = Effect.gen(function* () { · 61 unmodified lines | ||
| 124 | 132 | }); |
| 125 | 133 | |
| 126 | 134 | export const recordStartupHeartbeat = Effect.gen(function* () { |
| 127 | − const analytics = yield* AnalyticsService; | |
| 128 | − const projectionSnapshotQuery = yield* ProjectionSnapshotQuery; | |
| 135 | + const analytics = yield* AnalyticsService.AnalyticsService; | |
| 136 | + const projectionSnapshotQuery = yield* ProjectionSnapshotQuery.ProjectionSnapshotQuery; | |
| 129 | 137 | |
| 130 | 138 | const { threadCount, projectCount } = yield* projectionSnapshotQuery.getCounts().pipe( |
| 131 | 139 | Effect.catch((cause) => |
| @@ -160,7 +168,7 @@ export const getAutoBootstrapDefaultModelSelection = (): ModelSelection => ({ · 28 unmodified lines | ||
| 160 | 168 | }); |
| 161 | 169 | |
| 162 | 170 | export const resolveWelcomeBase = Effect.gen(function* () { |
| 163 | − const serverConfig = yield* ServerConfig; | |
| 171 | + const serverConfig = yield* ServerConfig.ServerConfig; | |
| 164 | 172 | const segments = serverConfig.cwd.split(/[/\\]/).filter(Boolean); |
| 165 | 173 | const projectName = segments[segments.length - 1] ?? "project"; |
| 166 | 174 | |
| @@ -173,9 +181,9 @@ export const resolveWelcomeBase = Effect.gen(function* () { · 6 unmodified lines | ||
| 173 | 181 | export const resolveAutoBootstrapWelcomeTargets = Effect.gen(function* () { |
| 174 | 182 | const crypto = yield* Crypto.Crypto; |
| 175 | 183 | const randomUUID = crypto.randomUUIDv4; |
| 176 | − const serverConfig = yield* ServerConfig; | |
| 177 | − const projectionReadModelQuery = yield* ProjectionSnapshotQuery; | |
| 178 | − const orchestrationEngine = yield* OrchestrationEngineService; | |
| 184 | + const serverConfig = yield* ServerConfig.ServerConfig; | |
| 185 | + const projectionReadModelQuery = yield* ProjectionSnapshotQuery.ProjectionSnapshotQuery; | |
| 186 | + const orchestrationEngine = yield* OrchestrationEngine.OrchestrationEngineService; | |
| 179 | 187 | const path = yield* Path.Path; |
| 180 | 188 | |
| 181 | 189 | let bootstrapProjectId: ProjectId | undefined; |
| @@ -243,7 +251,7 @@ export const resolveAutoBootstrapWelcomeTargets = Effect.gen(function* () { · 61 unmodified lines | ||
| 243 | 251 | }); |
| 244 | 252 | |
| 245 | 253 | const resolveStartupBrowserTarget = Effect.gen(function* () { |
| 246 | − const serverConfig = yield* ServerConfig; | |
| 254 | + const serverConfig = yield* ServerConfig.ServerConfig; | |
| 247 | 255 | const serverAuth = yield* EnvironmentAuth.EnvironmentAuth; |
| 248 | 256 | const localUrl = `http://localhost:${serverConfig.port}`; |
| 249 | 257 | const bindUrl = |
| @@ -260,7 +268,7 @@ const resolveStartupBrowserTarget = Effect.gen(function* () { · 10 unmodified lines | ||
| 260 | 268 | |
| 261 | 269 | const maybeOpenBrowser = (target: string) => |
| 262 | 270 | Effect.gen(function* () { |
| 263 | − const serverConfig = yield* ServerConfig; | |
| 271 | + const serverConfig = yield* ServerConfig.ServerConfig; | |
| 264 | 272 | if (serverConfig.noBrowser) { |
| 265 | 273 | return; |
| 266 | 274 | } |
| @@ -281,14 +289,14 @@ const runStartupPhase = <A, E, R>(phase: string, effect: Effect.Effect<A, E, R>) · 14 unmodified lines | ||
| 281 | 289 | Effect.withSpan(`server.startup.${phase}`), |
| 282 | 290 | ); |
| 283 | 291 | |
| 284 | −export const makeServerRuntimeStartup = Effect.gen(function* () { | |
| 285 | − const serverConfig = yield* ServerConfig; | |
| 286 | − const keybindings = yield* Keybindings; | |
| 287 | − const orchestrationReactor = yield* OrchestrationReactor; | |
| 288 | − const providerSessionReaper = yield* ProviderSessionReaper; | |
| 289 | − const lifecycleEvents = yield* ServerLifecycleEvents; | |
| 290 | − const serverSettings = yield* ServerSettingsService; | |
| 291 | − const serverEnvironment = yield* ServerEnvironment; | |
| 292 | +const make = Effect.gen(function* () { | |
| 293 | + const serverConfig = yield* ServerConfig.ServerConfig; | |
| 294 | + const keybindings = yield* Keybindings.Keybindings; | |
| 295 | + const orchestrationReactor = yield* OrchestrationReactor.OrchestrationReactor; | |
| 296 | + const providerSessionReaper = yield* ProviderSessionReaper.ProviderSessionReaper; | |
| 297 | + const lifecycleEvents = yield* ServerLifecycleEvents.ServerLifecycleEvents; | |
| 298 | + const serverSettings = yield* ServerSettings.ServerSettingsService; | |
| 299 | + const serverEnvironment = yield* ServerEnvironment.ServerEnvironment; | |
| 292 | 300 | const crypto = yield* Crypto.Crypto; |
| 293 | 301 | |
| 294 | 302 | const commandGate = yield* makeCommandGate; |
| @@ -409,7 +417,7 @@ export const makeServerRuntimeStartup = Effect.gen(function* () { · 114 unmodified lines | ||
| 409 | 417 | const startupExit = yield* Effect.exit(startup); |
| 410 | 418 | if (Exit.isFailure(startupExit)) { |
| 411 | 419 | const error = new ServerRuntimeStartupError({ |
| 412 | − message: "Server runtime startup failed before command readiness.", | |
| 420 | + stage: "command-readiness", | |
| 413 | 421 | cause: startupExit.cause, |
| 414 | 422 | }); |
| 415 | 423 | yield* Effect.logError("server runtime startup failed", { cause: startupExit.cause }); |
| @@ -461,10 +469,7 @@ export const makeServerRuntimeStartup = Effect.gen(function* () { · 45 unmodified lines | ||
| 461 | 469 | awaitCommandReady: commandGate.awaitCommandReady, |
| 462 | 470 | markHttpListening: Deferred.succeed(httpListening, undefined), |
| 463 | 471 | enqueueCommand: commandGate.enqueueCommand, |
| 464 | − } satisfies ServerRuntimeStartupShape; | |
| 472 | + } satisfies ServerRuntimeStartup["Service"]; | |
| 465 | 473 | }); |
| 466 | 474 | |
| 467 | −export const ServerRuntimeStartupLive = Layer.effect( | |
| 468 | − ServerRuntimeStartup, | |
| 469 | − makeServerRuntimeStartup, | |
| 470 | −); | |
| 475 | +export const layer = Layer.effect(ServerRuntimeStartup, make); | |
| @@ -5,7 +5,7 @@ import * as Option from "effect/Option"; · 4 unmodified lines | ||
| 5 | 5 | import * as Schema from "effect/Schema"; |
| 6 | 6 | |
| 7 | 7 | import { writeFileStringAtomically } from "./atomicWrite.ts"; |
| 8 | −import { type ServerConfigShape } from "./config.ts"; | |
| 8 | +import type * as ServerConfig from "./config.ts"; | |
| 9 | 9 | import { formatHostForUrl, isWildcardHost } from "./startupAccess.ts"; |
| 10 | 10 | |
| 11 | 11 | export const PersistedServerRuntimeState = Schema.Struct({ |
| @@ -23,7 +23,7 @@ const decodePersistedServerRuntimeState = Schema.decodeUnknownEffect( · 11 unmodified lines | ||
| 23 | 23 | ); |
| 24 | 24 | |
| 25 | 25 | const runtimeOriginForConfig = ( |
| 26 | − config: Pick<ServerConfigShape, "host">, | |
| 26 | + config: Pick<ServerConfig.ServerConfig["Service"], "host">, | |
| 27 | 27 | port: number, |
| 28 | 28 | ): PersistedServerRuntimeState["origin"] => { |
| 29 | 29 | const hostname = |
| @@ -32,7 +32,7 @@ const runtimeOriginForConfig = ( · 2 unmodified lines | ||
| 32 | 32 | }; |
| 33 | 33 | |
| 34 | 34 | export const makePersistedServerRuntimeState = (input: { |
| 35 | − readonly config: Pick<ServerConfigShape, "host">; | |
| 35 | + readonly config: Pick<ServerConfig.ServerConfig["Service"], "host">; | |
| 36 | 36 | readonly port: number; |
| 37 | 37 | }): Effect.Effect<PersistedServerRuntimeState> => |
| 38 | 38 | Effect.map(DateTime.now, (now) => ({ |
| @@ -13,14 +13,16 @@ import * as Duration from "effect/Duration"; · 12 unmodified lines | ||
| 13 | 13 | import * as FileSystem from "effect/FileSystem"; |
| 14 | 14 | import * as Layer from "effect/Layer"; |
| 15 | 15 | import * as Schema from "effect/Schema"; |
| 16 | −import { ServerConfig } from "./config.ts"; | |
| 17 | −import { ServerSettingsLive, ServerSettingsService } from "./serverSettings.ts"; | |
| 16 | +import * as ServerSecretStore from "./auth/ServerSecretStore.ts"; | |
| 17 | +import * as ServerConfig from "./config.ts"; | |
| 18 | +import * as ServerSettingsModule from "./serverSettings.ts"; | |
| 18 | 19 | |
| 19 | 20 | const decodeSettingsPatch = Schema.decodeUnknownEffect(ServerSettingsPatch); |
| 20 | 21 | const decodeServerSettings = Schema.decodeUnknownEffect(ServerSettings); |
| 21 | 22 | |
| 22 | 23 | const makeServerSettingsLayer = () => |
| 23 | − ServerSettingsLive.pipe( | |
| 24 | + ServerSettingsModule.layer.pipe( | |
| 25 | + Layer.provide(ServerSecretStore.layer), | |
| 24 | 26 | Layer.provideMerge( |
| 25 | 27 | Layer.fresh( |
| 26 | 28 | ServerConfig.layerTest(process.cwd(), { |
| @@ -77,7 +79,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { · 50 unmodified lines | ||
| 77 | 79 | |
| 78 | 80 | it.effect("deep merges nested settings updates without dropping siblings", () => |
| 79 | 81 | Effect.gen(function* () { |
| 80 | − const serverSettings = yield* ServerSettingsService; | |
| 82 | + const serverSettings = yield* ServerSettingsModule.ServerSettingsService; | |
| 81 | 83 | |
| 82 | 84 | yield* serverSettings.updateSettings({ |
| 83 | 85 | providers: { |
| @@ -145,7 +147,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { · 61 unmodified lines | ||
| 145 | 147 | |
| 146 | 148 | it.effect("preserves model when switching providers via textGenerationModelSelection", () => |
| 147 | 149 | Effect.gen(function* () { |
| 148 | − const serverSettings = yield* ServerSettingsService; | |
| 150 | + const serverSettings = yield* ServerSettingsModule.ServerSettingsService; | |
| 149 | 151 | |
| 150 | 152 | // Start with Claude text generation selection |
| 151 | 153 | yield* serverSettings.updateSettings({ |
| @@ -183,7 +185,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { · 31 unmodified lines | ||
| 183 | 185 | |
| 184 | 186 | it.effect("preserves custom provider instance text generation selections", () => |
| 185 | 187 | Effect.gen(function* () { |
| 186 | − const serverSettings = yield* ServerSettingsService; | |
| 188 | + const serverSettings = yield* ServerSettingsModule.ServerSettingsService; | |
| 187 | 189 | |
| 188 | 190 | const next = yield* serverSettings.updateSettings({ |
| 189 | 191 | providerInstances: { |
| @@ -210,7 +212,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { · 20 unmodified lines | ||
| 210 | 212 | "uses explicit provider instance enabled state over legacy provider enabled state", |
| 211 | 213 | () => |
| 212 | 214 | Effect.gen(function* () { |
| 213 | − const serverSettings = yield* ServerSettingsService; | |
| 215 | + const serverSettings = yield* ServerSettingsModule.ServerSettingsService; | |
| 214 | 216 | const instanceId = ProviderInstanceId.make("claude_openrouter"); |
| 215 | 217 | |
| 216 | 218 | const next = yield* serverSettings.updateSettings({ |
| @@ -241,7 +243,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { · 24 unmodified lines | ||
| 241 | 243 | |
| 242 | 244 | it.effect("preserves enabled text generation selections for non-built-in drivers", () => |
| 243 | 245 | Effect.gen(function* () { |
| 244 | − const serverSettings = yield* ServerSettingsService; | |
| 246 | + const serverSettings = yield* ServerSettingsModule.ServerSettingsService; | |
| 245 | 247 | const instanceId = ProviderInstanceId.make("openrouter_text"); |
| 246 | 248 | |
| 247 | 249 | const next = yield* serverSettings.updateSettings({ |
| @@ -267,7 +269,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { · 19 unmodified lines | ||
| 267 | 269 | |
| 268 | 270 | it.effect("drops stale text generation options when resetting model selection", () => |
| 269 | 271 | Effect.gen(function* () { |
| 270 | − const serverSettings = yield* ServerSettingsService; | |
| 272 | + const serverSettings = yield* ServerSettingsModule.ServerSettingsService; | |
| 271 | 273 | |
| 272 | 274 | yield* serverSettings.updateSettings({ |
| 273 | 275 | textGenerationModelSelection: { |
| @@ -300,7 +302,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { · 26 unmodified lines | ||
| 300 | 302 | |
| 301 | 303 | it.effect("replaces provider instance maps when clearing optional fields", () => |
| 302 | 304 | Effect.gen(function* () { |
| 303 | − const serverSettings = yield* ServerSettingsService; | |
| 305 | + const serverSettings = yield* ServerSettingsModule.ServerSettingsService; | |
| 304 | 306 | const codexId = ProviderInstanceId.make("codex"); |
| 305 | 307 | |
| 306 | 308 | yield* serverSettings.updateSettings({ |
| @@ -337,7 +339,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { · 30 unmodified lines | ||
| 337 | 339 | |
| 338 | 340 | it.effect("trims provider path settings when updates are applied", () => |
| 339 | 341 | Effect.gen(function* () { |
| 340 | − const serverSettings = yield* ServerSettingsService; | |
| 342 | + const serverSettings = yield* ServerSettingsModule.ServerSettingsService; | |
| 341 | 343 | |
| 342 | 344 | const next = yield* serverSettings.updateSettings({ |
| 343 | 345 | providers: { |
| @@ -382,7 +384,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { · 38 unmodified lines | ||
| 382 | 384 | |
| 383 | 385 | it.effect("trims observability settings when updates are applied", () => |
| 384 | 386 | Effect.gen(function* () { |
| 385 | − const serverSettings = yield* ServerSettingsService; | |
| 387 | + const serverSettings = yield* ServerSettingsModule.ServerSettingsService; | |
| 386 | 388 | |
| 387 | 389 | const next = yield* serverSettings.updateSettings({ |
| 388 | 390 | addProjectBaseDirectory: " ~/Development ", |
| @@ -402,7 +404,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { · 13 unmodified lines | ||
| 402 | 404 | |
| 403 | 405 | it.effect("defaults blank binary paths to provider executables", () => |
| 404 | 406 | Effect.gen(function* () { |
| 405 | − const serverSettings = yield* ServerSettingsService; | |
| 407 | + const serverSettings = yield* ServerSettingsModule.ServerSettingsService; | |
| 406 | 408 | |
| 407 | 409 | const next = yield* serverSettings.updateSettings({ |
| 408 | 410 | providers: { |
| @@ -422,8 +424,8 @@ it.layer(NodeServices.layer)("server settings", (it) => { · 13 unmodified lines | ||
| 422 | 424 | |
| 423 | 425 | it.effect("writes only non-default server settings to disk", () => |
| 424 | 426 | Effect.gen(function* () { |
| 425 | − const serverSettings = yield* ServerSettingsService; | |
| 426 | − const serverConfig = yield* ServerConfig; | |
| 427 | + const serverSettings = yield* ServerSettingsModule.ServerSettingsService; | |
| 428 | + const serverConfig = yield* ServerConfig.ServerConfig; | |
| 427 | 429 | const fileSystem = yield* FileSystem.FileSystem; |
| 428 | 430 | const next = yield* serverSettings.updateSettings({ |
| 429 | 431 | addProjectBaseDirectory: "~/Development", |
| @@ -469,8 +471,8 @@ it.layer(NodeServices.layer)("server settings", (it) => { · 39 unmodified lines | ||
| 469 | 471 | |
| 470 | 472 | it.effect("stores sensitive provider instance environment values outside settings.json", () => |
| 471 | 473 | Effect.gen(function* () { |
| 472 | − const serverSettings = yield* ServerSettingsService; | |
| 473 | − const serverConfig = yield* ServerConfig; | |
| 474 | + const serverSettings = yield* ServerSettingsModule.ServerSettingsService; | |
| 475 | + const serverConfig = yield* ServerConfig.ServerConfig; | |
| 474 | 476 | const fileSystem = yield* FileSystem.FileSystem; |
| 475 | 477 | const instanceId = ProviderInstanceId.make("codex_personal"); |
| 476 | 478 | |
| @@ -26,26 +26,26 @@ import { · 25 unmodified lines | ||
| 26 | 26 | type ServerSettingsPatch, |
| 27 | 27 | } from "@t3tools/contracts"; |
| 28 | 28 | import * as Cache from "effect/Cache"; |
| 29 | +import * as Cause from "effect/Cause"; | |
| 30 | +import * as Context from "effect/Context"; | |
| 29 | 31 | import * as Deferred from "effect/Deferred"; |
| 30 | 32 | import * as Duration from "effect/Duration"; |
| 33 | +import * as Equal from "effect/Equal"; | |
| 31 | 34 | import * as Effect from "effect/Effect"; |
| 32 | 35 | import * as Exit from "effect/Exit"; |
| 33 | 36 | import * as FileSystem from "effect/FileSystem"; |
| 34 | 37 | import * as Layer from "effect/Layer"; |
| 35 | 38 | import * as Option from "effect/Option"; |
| 36 | 39 | import * as Path from "effect/Path"; |
| 37 | −import * as Equal from "effect/Equal"; | |
| 38 | 40 | import * as PubSub from "effect/PubSub"; |
| 39 | 41 | import * as Ref from "effect/Ref"; |
| 40 | 42 | import * as Schema from "effect/Schema"; |
| 41 | 43 | import * as SchemaIssue from "effect/SchemaIssue"; |
| 44 | +import * as Semaphore from "effect/Semaphore"; | |
| 42 | 45 | import * as Scope from "effect/Scope"; |
| 43 | −import * as Context from "effect/Context"; | |
| 44 | 46 | import * as Stream from "effect/Stream"; |
| 45 | −import * as Cause from "effect/Cause"; | |
| 46 | −import * as Semaphore from "effect/Semaphore"; | |
| 47 | 47 | import { writeFileStringAtomically } from "./atomicWrite.ts"; |
| 48 | −import { ServerConfig } from "./config.ts"; | |
| 48 | +import * as ServerConfig from "./config.ts"; | |
| 49 | 49 | import { type DeepPartial, deepMerge } from "@t3tools/shared/Struct"; |
| 50 | 50 | import { fromJsonStringPretty, fromLenientJson } from "@t3tools/shared/schemaJson"; |
| 51 | 51 | import { applyServerSettingsPatch } from "@t3tools/shared/serverSettings"; |
| @@ -109,59 +109,60 @@ export function redactServerSettingsForClient(settings: ServerSettings): ServerS · 57 unmodified lines | ||
| 109 | 109 | return { ...settings, providerInstances }; |
| 110 | 110 | } |
| 111 | 111 | |
| 112 | −export interface ServerSettingsShape { | |
| 113 | − /** Start the settings runtime and attach file watching. */ | |
| 114 | − readonly start: Effect.Effect<void, ServerSettingsError>; | |
| 115 | − | |
| 116 | − /** Await settings runtime readiness. */ | |
| 117 | − readonly ready: Effect.Effect<void, ServerSettingsError>; | |
| 112 | +export class ServerSettingsService extends Context.Service< | |
| 113 | + ServerSettingsService, | |
| 114 | + { | |
| 115 | + /** Start the settings runtime and attach file watching. */ | |
| 116 | + readonly start: Effect.Effect<void, ServerSettingsError>; | |
| 118 | 117 | |
| 119 | − /** Read the current settings. */ | |
| 120 | − readonly getSettings: Effect.Effect<ServerSettings, ServerSettingsError>; | |
| 118 | + /** Await settings runtime readiness. */ | |
| 119 | + readonly ready: Effect.Effect<void, ServerSettingsError>; | |
| 121 | 120 | |
| 122 | − /** Patch settings and persist. Returns the new full settings object. */ | |
| 123 | − readonly updateSettings: ( | |
| 124 | − patch: ServerSettingsPatch, | |
| 125 | − ) => Effect.Effect<ServerSettings, ServerSettingsError>; | |
| 121 | + /** Read the current settings. */ | |
| 122 | + readonly getSettings: Effect.Effect<ServerSettings, ServerSettingsError>; | |
| 126 | 123 | |
| 127 | − /** Stream of settings change events. */ | |
| 128 | − readonly streamChanges: Stream.Stream<ServerSettings>; | |
| 129 | −} | |
| 124 | + /** Patch settings and persist. Returns the new full settings object. */ | |
| 125 | + readonly updateSettings: ( | |
| 126 | + patch: ServerSettingsPatch, | |
| 127 | + ) => Effect.Effect<ServerSettings, ServerSettingsError>; | |
| 130 | 128 | |
| 131 | −export class ServerSettingsService extends Context.Service< | |
| 132 | − ServerSettingsService, | |
| 133 | − ServerSettingsShape | |
| 129 | + /** Stream of settings change events. */ | |
| 130 | + readonly streamChanges: Stream.Stream<ServerSettings>; | |
| 131 | + } | |
| 134 | 132 | >()("t3/serverSettings/ServerSettingsService") { |
| 135 | − static readonly layerTest = (overrides: DeepPartial<ServerSettings> = {}) => | |
| 136 | − Layer.effect( | |
| 137 | − ServerSettingsService, | |
| 138 | − Effect.gen(function* () { | |
| 139 | − const { automaticGitFetchInterval, ...overridesForMerge } = overrides; | |
| 140 | − const merged = deepMerge(DEFAULT_SERVER_SETTINGS, overridesForMerge); | |
| 141 | − const initialSettings = yield* normalizeServerSettings({ | |
| 142 | − ...merged, | |
| 143 | − ...(automaticGitFetchInterval !== undefined | |
| 144 | − ? { automaticGitFetchInterval: automaticGitFetchInterval as Duration.Duration } | |
| 145 | − : {}), | |
| 146 | − }); | |
| 147 | − const currentSettingsRef = yield* Ref.make<ServerSettings>(initialSettings); | |
| 148 | − | |
| 149 | − return { | |
| 150 | − start: Effect.void, | |
| 151 | − ready: Effect.void, | |
| 152 | − getSettings: Ref.get(currentSettingsRef), | |
| 153 | − updateSettings: (patch) => | |
| 154 | − Ref.get(currentSettingsRef).pipe( | |
| 155 | − Effect.map((currentSettings) => applyServerSettingsPatch(currentSettings, patch)), | |
| 156 | − Effect.flatMap(normalizeServerSettings), | |
| 157 | − Effect.tap((nextSettings) => Ref.set(currentSettingsRef, nextSettings)), | |
| 158 | − ), | |
| 159 | − streamChanges: Stream.empty, | |
| 160 | − } satisfies ServerSettingsShape; | |
| 161 | − }), | |
| 162 | − ); | |
| 133 | + /** @deprecated Import and use `layerTest` from this module. */ | |
| 134 | + static readonly layerTest = (overrides: DeepPartial<ServerSettings> = {}) => layerTest(overrides); | |
| 163 | 135 | } |
| 164 | 136 | |
| 137 | +const makeTest = (overrides: DeepPartial<ServerSettings> = {}) => | |
| 138 | + Effect.gen(function* () { | |
| 139 | + const { automaticGitFetchInterval, ...overridesForMerge } = overrides; | |
| 140 | + const merged = deepMerge(DEFAULT_SERVER_SETTINGS, overridesForMerge); | |
| 141 | + const initialSettings = yield* normalizeServerSettings({ | |
| 142 | + ...merged, | |
| 143 | + ...(automaticGitFetchInterval !== undefined | |
| 144 | + ? { automaticGitFetchInterval: automaticGitFetchInterval as Duration.Duration } | |
| 145 | + : {}), | |
| 146 | + }); | |
| 147 | + const currentSettingsRef = yield* Ref.make<ServerSettings>(initialSettings); | |
| 148 | + | |
| 149 | + return { | |
| 150 | + start: Effect.void, | |
| 151 | + ready: Effect.void, | |
| 152 | + getSettings: Ref.get(currentSettingsRef), | |
| 153 | + updateSettings: (patch) => | |
| 154 | + Ref.get(currentSettingsRef).pipe( | |
| 155 | + Effect.map((currentSettings) => applyServerSettingsPatch(currentSettings, patch)), | |
| 156 | + Effect.flatMap(normalizeServerSettings), | |
| 157 | + Effect.tap((nextSettings) => Ref.set(currentSettingsRef, nextSettings)), | |
| 158 | + ), | |
| 159 | + streamChanges: Stream.empty, | |
| 160 | + } satisfies ServerSettingsService["Service"]; | |
| 161 | + }); | |
| 162 | + | |
| 163 | +export const layerTest = (overrides: DeepPartial<ServerSettings> = {}) => | |
| 164 | + Layer.effect(ServerSettingsService, makeTest(overrides)); | |
| 165 | + | |
| 165 | 166 | const ServerSettingsJson = fromLenientJson(ServerSettings); |
| 166 | 167 | const decodeServerSettingsJsonExit = Schema.decodeUnknownExit(ServerSettingsJson); |
| 167 | 168 | |
| @@ -255,8 +256,8 @@ function stripDefaultServerSettings(current: unknown, defaults: unknown): unknow · 87 unmodified lines | ||
| 255 | 256 | return Object.is(current, defaults) ? undefined : current; |
| 256 | 257 | } |
| 257 | 258 | |
| 258 | −const makeServerSettings = Effect.gen(function* () { | |
| 259 | − const { settingsPath } = yield* ServerConfig; | |
| 259 | +const make = Effect.gen(function* () { | |
| 260 | + const { settingsPath } = yield* ServerConfig.ServerConfig; | |
| 260 | 261 | const fs = yield* FileSystem.FileSystem; |
| 261 | 262 | const pathService = yield* Path.Path; |
| 262 | 263 | const secretStore = yield* ServerSecretStore.ServerSecretStore; |
| @@ -578,9 +579,7 @@ const makeServerSettings = Effect.gen(function* () { · 315 unmodified lines | ||
| 578 | 579 | Stream.map(resolveTextGenerationProvider), |
| 579 | 580 | ); |
| 580 | 581 | }, |
| 581 | − } satisfies ServerSettingsShape; | |
| 582 | + } satisfies ServerSettingsService["Service"]; | |
| 582 | 583 | }); |
| 583 | 584 | |
| 584 | −export const ServerSettingsLive = Layer.effect(ServerSettingsService, makeServerSettings).pipe( | |
| 585 | − Layer.provide(ServerSecretStore.layer), | |
| 586 | −); | |
| 585 | +export const layer = Layer.effect(ServerSettingsService, make); | |
| @@ -100,6 +100,41 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { · 99 unmodified lines | ||
| 100 | 100 | assert.deepStrictEqual(paths, ["complete.txt", "final.txt"]); |
| 101 | 101 | }), |
| 102 | 102 | ); |
| 103 | + | |
| 104 | + it.effect("honors whitespace filtering for worktree and branch previews", () => | |
| 105 | + Effect.gen(function* () { | |
| 106 | + const cwd = yield* makeTmpDir(); | |
| 107 | + const { initialBranch } = yield* initRepoWithCommit(cwd); | |
| 108 | + const driver = yield* GitVcsDriver.GitVcsDriver; | |
| 109 | + yield* git(cwd, ["checkout", "-b", "feature/whitespace"]); | |
| 110 | + yield* writeTextFile(cwd, "README.md", "# test\n"); | |
| 111 | + yield* git(cwd, ["add", "README.md"]); | |
| 112 | + yield* git(cwd, ["commit", "-m", "change whitespace"]); | |
| 113 | + yield* writeTextFile(cwd, "README.md", "# test\n"); | |
| 114 | + | |
| 115 | + const included = yield* driver.getReviewDiffPreview({ | |
| 116 | + cwd, | |
| 117 | + baseRef: initialBranch, | |
| 118 | + ignoreWhitespace: false, | |
| 119 | + }); | |
| 120 | + const ignored = yield* driver.getReviewDiffPreview({ | |
| 121 | + cwd, | |
| 122 | + baseRef: initialBranch, | |
| 123 | + ignoreWhitespace: true, | |
| 124 | + }); | |
| 125 | + | |
| 126 | + assert.isNotEmpty(included.sources.find((source) => source.kind === "working-tree")?.diff); | |
| 127 | + assert.isNotEmpty(included.sources.find((source) => source.kind === "branch-range")?.diff); | |
| 128 | + assert.strictEqual( | |
| 129 | + ignored.sources.find((source) => source.kind === "working-tree")?.diff, | |
| 130 | + "", | |
| 131 | + ); | |
| 132 | + assert.strictEqual( | |
| 133 | + ignored.sources.find((source) => source.kind === "branch-range")?.diff, | |
| 134 | + "", | |
| 135 | + ); | |
| 136 | + }), | |
| 137 | + ); | |
| 103 | 138 | }); |
| 104 | 139 | |
| 105 | 140 | describe("repository status", () => { |
| @@ -342,6 +377,44 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { · 236 unmodified lines | ||
| 342 | 377 | }); |
| 343 | 378 | |
| 344 | 379 | describe("refName operations", () => { |
| 380 | + it.effect("optionally includes remote refs that match local branches", () => | |
| 381 | + Effect.gen(function* () { | |
| 382 | + const cwd = yield* makeTmpDir(); | |
| 383 | + const remote = yield* makeTmpDir("git-vcs-driver-remote-"); | |
| 384 | + const { initialBranch } = yield* initRepoWithCommit(cwd); | |
| 385 | + yield* git(remote, ["init", "--bare"]); | |
| 386 | + yield* git(cwd, ["remote", "add", "origin", remote]); | |
| 387 | + yield* git(cwd, ["push", "-u", "origin", initialBranch]); | |
| 388 | + const driver = yield* GitVcsDriver.GitVcsDriver; | |
| 389 | + | |
| 390 | + const deduplicated = yield* driver.listRefs({ cwd }); | |
| 391 | + assert.equal( | |
| 392 | + deduplicated.refs.some((ref) => ref.name === `origin/${initialBranch}`), | |
| 393 | + false, | |
| 394 | + ); | |
| 395 | + | |
| 396 | + const complete = yield* driver.listRefs({ cwd, includeMatchingRemoteRefs: true }); | |
| 397 | + assert.equal( | |
| 398 | + complete.refs.some((ref) => ref.name === initialBranch), | |
| 399 | + true, | |
| 400 | + ); | |
| 401 | + assert.equal( | |
| 402 | + complete.refs.some((ref) => ref.name === `origin/${initialBranch}`), | |
| 403 | + true, | |
| 404 | + ); | |
| 405 | + | |
| 406 | + const remoteOnly = yield* driver.listRefs({ | |
| 407 | + cwd, | |
| 408 | + includeMatchingRemoteRefs: true, | |
| 409 | + refKind: "remote", | |
| 410 | + limit: 1, | |
| 411 | + }); | |
| 412 | + assert.equal(remoteOnly.refs.length, 1); | |
| 413 | + assert.equal(remoteOnly.refs[0]?.name, `origin/${initialBranch}`); | |
| 414 | + assert.equal(remoteOnly.refs[0]?.isRemote, true); | |
| 415 | + }), | |
| 416 | + ); | |
| 417 | + | |
| 345 | 418 | it.effect("creates, checks out, renames, and lists refs", () => |
| 346 | 419 | Effect.gen(function* () { |
| 347 | 420 | const cwd = yield* makeTmpDir(); |
| @@ -1817,7 +1817,14 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* · 1816 unmodified lines | ||
| 1817 | 1817 | const dirtyTrackedResult = yield* executeGit( |
| 1818 | 1818 | "GitVcsDriver.getReviewDiffPreview.dirtyTracked", |
| 1819 | 1819 | input.cwd, |
| 1820 | − ["diff", "--patch", "--minimal", "HEAD", "--"], | |
| 1820 | + [ | |
| 1821 | + "diff", | |
| 1822 | + "--patch", | |
| 1823 | + "--minimal", | |
| 1824 | + ...(input.ignoreWhitespace ? ["--ignore-all-space"] : []), | |
| 1825 | + "HEAD", | |
| 1826 | + "--", | |
| 1827 | + ], | |
| 1821 | 1828 | { |
| 1822 | 1829 | maxOutputBytes: REVIEW_DIFF_PATCH_MAX_OUTPUT_BYTES, |
| 1823 | 1830 | appendTruncationMarker: true, |
| @@ -1843,7 +1850,13 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* · 19 unmodified lines | ||
| 1843 | 1850 | ? yield* executeGit( |
| 1844 | 1851 | "GitVcsDriver.getReviewDiffPreview.base", |
| 1845 | 1852 | input.cwd, |
| 1846 | − ["diff", "--patch", "--minimal", `${baseRef}...HEAD`], | |
| 1853 | + [ | |
| 1854 | + "diff", | |
| 1855 | + "--patch", | |
| 1856 | + "--minimal", | |
| 1857 | + ...(input.ignoreWhitespace ? ["--ignore-all-space"] : []), | |
| 1858 | + `${baseRef}...HEAD`, | |
| 1859 | + ], | |
| 1847 | 1860 | { |
| 1848 | 1861 | maxOutputBytes: REVIEW_DIFF_PATCH_MAX_OUTPUT_BYTES, |
| 1849 | 1862 | appendTruncationMarker: true, |
| @@ -2127,11 +2140,17 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* · 277 unmodified lines | ||
| 2127 | 2140 | }) |
| 2128 | 2141 | : []; |
| 2129 | 2142 | |
| 2143 | + const allBranches = input.includeMatchingRemoteRefs | |
| 2144 | + ? [...localBranches, ...remoteBranches] | |
| 2145 | + : dedupeRemoteBranchesWithLocalMatches([...localBranches, ...remoteBranches]); | |
| 2146 | + const branchesForKind = | |
| 2147 | + input.refKind === "local" | |
| 2148 | + ? allBranches.filter((ref) => !ref.isRemote) | |
| 2149 | + : input.refKind === "remote" | |
| 2150 | + ? allBranches.filter((ref) => ref.isRemote) | |
| 2151 | + : allBranches; | |
| 2130 | 2152 | const refs = paginateBranches({ |
| 2131 | − refs: filterBranchesForListQuery( | |
| 2132 | − dedupeRemoteBranchesWithLocalMatches([...localBranches, ...remoteBranches]), | |
| 2133 | − input.query, | |
| 2134 | − ), | |
| 2153 | + refs: filterBranchesForListQuery(branchesForKind, input.query), | |
| 2135 | 2154 | cursor: input.cursor, |
| 2136 | 2155 | limit: input.limit, |
| 2137 | 2156 | }); |
| @@ -1489,7 +1489,7 @@ const makeWsRpcLayer = (currentSession: AuthenticatedSession) => · 1488 unmodified lines | ||
| 1489 | 1489 | [WS_METHODS.previewAutomationConnect]: (input) => |
| 1490 | 1490 | observeRpcStreamEffect( |
| 1491 | 1491 | WS_METHODS.previewAutomationConnect, |
| 1492 | − previewAutomationBroker.connect(input.clientId), | |
| 1492 | + previewAutomationBroker.connect(input), | |
| 1493 | 1493 | { "rpc.aggregate": "preview-automation" }, |
| 1494 | 1494 | ), |
| 1495 | 1495 | [WS_METHODS.previewAutomationRespond]: (input) => |
| @@ -1507,7 +1507,7 @@ const makeWsRpcLayer = (currentSession: AuthenticatedSession) => · 11 unmodified lines | ||
| 1507 | 1507 | [WS_METHODS.previewAutomationClearOwner]: (input) => |
| 1508 | 1508 | observeRpcEffect( |
| 1509 | 1509 | WS_METHODS.previewAutomationClearOwner, |
| 1510 | − previewAutomationBroker.clearOwner(input.clientId), | |
| 1510 | + previewAutomationBroker.clearOwner(input), | |
| 1511 | 1511 | { "rpc.aggregate": "preview-automation" }, |
| 1512 | 1512 | ), |
| 1513 | 1513 | [WS_METHODS.subscribePreviewEvents]: (_input) => |
| @@ -42,7 +42,7 @@ import { nextTerminalId, resolveTerminalSessionLabel } from "@t3tools/shared/ter · 41 unmodified lines | ||
| 42 | 42 | import { Debouncer } from "@tanstack/react-pacer"; |
| 43 | 43 | import { useAtomValue } from "@effect/atom-react"; |
| 44 | 44 | import { lazy, memo, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react"; |
| 45 | −import { useNavigate, useSearch } from "@tanstack/react-router"; | |
| 45 | +import { useNavigate } from "@tanstack/react-router"; | |
| 46 | 46 | import { useShallow } from "zustand/react/shallow"; |
| 47 | 47 | import { |
| 48 | 48 | isAtomCommandInterrupted, |
| @@ -55,7 +55,7 @@ import * as Cause from "effect/Cause"; · 6 unmodified lines | ||
| 55 | 55 | import { AsyncResult } from "effect/unstable/reactivity"; |
| 56 | 56 | import { isElectron } from "../env"; |
| 57 | 57 | import { readLocalApi } from "../localApi"; |
| 58 | −import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch"; | |
| 58 | +import { useDiffPanelStore } from "../diffPanelStore"; | |
| 59 | 59 | import { |
| 60 | 60 | collapseExpandedComposerCursor, |
| 61 | 61 | parseStandaloneComposerSlashCommand, |
| @@ -104,7 +104,7 @@ import { buildTemporaryWorktreeBranchName } from "@t3tools/shared/git"; · 42 unmodified lines | ||
| 104 | 104 | import { useMediaQuery } from "../hooks/useMediaQuery"; |
| 105 | 105 | import { RIGHT_PANEL_INLINE_LAYOUT_MEDIA_QUERY } from "../rightPanelLayout"; |
| 106 | 106 | import { |
| 107 | − selectActiveRightPanelKindWithUrl, | |
| 107 | + selectActiveRightPanel, | |
| 108 | 108 | selectActiveRightPanelSurface, |
| 109 | 109 | selectThreadRightPanelState, |
| 110 | 110 | type RightPanelSurface, |
| @@ -1034,10 +1034,6 @@ function ChatViewContent(props: ChatViewProps) { · 923 unmodified lines | ||
| 1034 | 1034 | const timestampFormat = settings.timestampFormat; |
| 1035 | 1035 | const autoOpenPlanSidebar = settings.autoOpenPlanSidebar; |
| 1036 | 1036 | const navigate = useNavigate(); |
| 1037 | − const rawSearch = useSearch({ | |
| 1038 | − strict: false, | |
| 1039 | − select: (params) => parseDiffRouteSearch(params), | |
| 1040 | − }); | |
| 1041 | 1037 | const { resolvedTheme } = useTheme(); |
| 1042 | 1038 | // Granular store selectors — avoid subscribing to prompt changes. |
| 1043 | 1039 | const composerRuntimeMode = useComposerDraftStore( |
| @@ -1217,7 +1213,6 @@ function ChatViewContent(props: ChatViewProps) { · 173 unmodified lines | ||
| 1217 | 1213 | composerInteractionMode ?? activeThread?.interactionMode ?? DEFAULT_INTERACTION_MODE; |
| 1218 | 1214 | const isLocalDraftThread = !isServerThread && localDraftThread !== undefined; |
| 1219 | 1215 | const canCheckoutPullRequestIntoThread = isLocalDraftThread; |
| 1220 | − const diffOpen = rawSearch.diff === "1"; | |
| 1221 | 1216 | const activeThreadId = activeThread?.id ?? null; |
| 1222 | 1217 | const runningTerminalIds = useThreadRunningTerminalIds({ |
| 1223 | 1218 | environmentId: activeThread?.environmentId ?? null, |
| @@ -1259,8 +1254,9 @@ function ChatViewContent(props: ChatViewProps) { · 35 unmodified lines | ||
| 1259 | 1254 | ); |
| 1260 | 1255 | const activeThreadKey = activeThreadRef ? scopedThreadKey(activeThreadRef) : null; |
| 1261 | 1256 | const activeRightPanelKind = useRightPanelStore((state) => |
| 1262 | − selectActiveRightPanelKindWithUrl(state.byThreadKey, activeThreadRef, diffOpen), | |
| 1257 | + selectActiveRightPanel(state.byThreadKey, activeThreadRef), | |
| 1263 | 1258 | ); |
| 1259 | + const diffOpen = activeRightPanelKind === "diff"; | |
| 1264 | 1260 | const rightPanelState = useRightPanelStore((state) => |
| 1265 | 1261 | selectThreadRightPanelState(state.byThreadKey, activeThreadRef), |
| 1266 | 1262 | ); |
| @@ -1295,11 +1291,6 @@ function ChatViewContent(props: ChatViewProps) { · 28 unmodified lines | ||
| 1295 | 1291 | |
| 1296 | 1292 | const planSidebarOpen = activeRightPanelKind === "plan"; |
| 1297 | 1293 | |
| 1298 | − useEffect(() => { | |
| 1299 | − if (!activeThreadRef || !diffOpen) return; | |
| 1300 | − useRightPanelStore.getState().open(activeThreadRef, "diff"); | |
| 1301 | − }, [activeThreadRef, diffOpen]); | |
| 1302 | − | |
| 1303 | 1294 | const existingOpenTerminalThreadKeys = useMemo(() => { |
| 1304 | 1295 | const existingThreadKeys = new Set<string>([...serverThreadKeys, ...draftThreadKeys]); |
| 1305 | 1296 | return openTerminalThreadKeys.filter((nextThreadKey) => existingThreadKeys.has(nextThreadKey)); |
| @@ -2151,27 +2142,7 @@ function ChatViewContent(props: ChatViewProps) { · 845 unmodified lines | ||
| 2151 | 2142 | if (activeThreadRef) { |
| 2152 | 2143 | useRightPanelStore.getState().toggle(activeThreadRef, "diff"); |
| 2153 | 2144 | } |
| 2154 | − void navigate({ | |
| 2155 | − to: "/$environmentId/$threadId", | |
| 2156 | − params: { | |
| 2157 | − environmentId, | |
| 2158 | − threadId, | |
| 2159 | − }, | |
| 2160 | − replace: true, | |
| 2161 | − search: (previous) => { | |
| 2162 | − const rest = stripDiffSearchParams(previous); | |
| 2163 | − return diffOpen ? { ...rest, diff: undefined } : { ...rest, diff: "1" }; | |
| 2164 | − }, | |
| 2165 | − }); | |
| 2166 | − }, [ | |
| 2167 | − activeThreadRef, | |
| 2168 | − diffOpen, | |
| 2169 | − environmentId, | |
| 2170 | − isServerThread, | |
| 2171 | − navigate, | |
| 2172 | − onDiffPanelOpen, | |
| 2173 | − threadId, | |
| 2174 | − ]); | |
| 2145 | + }, [activeThreadRef, diffOpen, isServerThread, onDiffPanelOpen]); | |
| 2175 | 2146 | |
| 2176 | 2147 | const envLocked = Boolean( |
| 2177 | 2148 | activeThread && |
| @@ -2757,21 +2728,7 @@ function ChatViewContent(props: ChatViewProps) { · 579 unmodified lines | ||
| 2757 | 2728 | if (!activeThreadRef || !isServerThread || !isGitRepo) return; |
| 2758 | 2729 | useRightPanelStore.getState().open(activeThreadRef, "diff"); |
| 2759 | 2730 | onDiffPanelOpen?.(); |
| 2760 | − void navigate({ | |
| 2761 | − to: "/$environmentId/$threadId", | |
| 2762 | − params: { environmentId, threadId }, | |
| 2763 | − replace: true, | |
| 2764 | − search: (previous) => ({ ...stripDiffSearchParams(previous), diff: "1" }), | |
| 2765 | − }); | |
| 2766 | − }, [ | |
| 2767 | − activeThreadRef, | |
| 2768 | − environmentId, | |
| 2769 | − isGitRepo, | |
| 2770 | − isServerThread, | |
| 2771 | − navigate, | |
| 2772 | − onDiffPanelOpen, | |
| 2773 | − threadId, | |
| 2774 | − ]); | |
| 2731 | + }, [activeThreadRef, isGitRepo, isServerThread, onDiffPanelOpen]); | |
| 2775 | 2732 | const addFilesSurface = useCallback(() => { |
| 2776 | 2733 | if (!activeThreadRef || !activeProject) return; |
| 2777 | 2734 | useRightPanelStore.getState().open(activeThreadRef, "files"); |
| @@ -2789,30 +2746,13 @@ function ChatViewContent(props: ChatViewProps) { · 11 unmodified lines | ||
| 2789 | 2746 | useRightPanelStore.getState().close(activeThreadRef); |
| 2790 | 2747 | return; |
| 2791 | 2748 | } |
| 2792 | − if (diffOpen) { | |
| 2793 | − void navigate({ | |
| 2794 | − to: "/$environmentId/$threadId", | |
| 2795 | − params: { environmentId, threadId }, | |
| 2796 | − replace: true, | |
| 2797 | − search: (previous) => ({ ...stripDiffSearchParams(previous), diff: undefined }), | |
| 2798 | − }); | |
| 2799 | − } | |
| 2800 | 2749 | const activeTabId = activePreviewState.activeTabId; |
| 2801 | 2750 | if (activeTabId) { |
| 2802 | 2751 | useRightPanelStore.getState().openBrowser(activeThreadRef, activeTabId); |
| 2803 | 2752 | } else { |
| 2804 | 2753 | createBrowserSurface(); |
| 2805 | 2754 | } |
| 2806 | − }, [ | |
| 2807 | − activePreviewState.activeTabId, | |
| 2808 | − activeThreadRef, | |
| 2809 | − createBrowserSurface, | |
| 2810 | − diffOpen, | |
| 2811 | − environmentId, | |
| 2812 | − navigate, | |
| 2813 | − previewPanelOpen, | |
| 2814 | − threadId, | |
| 2815 | − ]); | |
| 2755 | + }, [activePreviewState.activeTabId, activeThreadRef, createBrowserSurface, previewPanelOpen]); | |
| 2816 | 2756 | const closePreviewPanel = useCallback(() => { |
| 2817 | 2757 | if (activeThreadRef) { |
| 2818 | 2758 | setMaximizedRightPanelThreadKey(null); |
| @@ -2936,31 +2876,9 @@ function ChatViewContent(props: ChatViewProps) { · 117 unmodified lines | ||
| 2936 | 2876 | } |
| 2937 | 2877 | if (surface.kind === "diff" && !diffOpen) { |
| 2938 | 2878 | onDiffPanelOpen?.(); |
| 2939 | − void navigate({ | |
| 2940 | − to: "/$environmentId/$threadId", | |
| 2941 | − params: { environmentId, threadId }, | |
| 2942 | − replace: true, | |
| 2943 | − search: (previous) => ({ ...stripDiffSearchParams(previous), diff: "1" }), | |
| 2944 | − }); | |
| 2945 | − } else if (surface.kind !== "diff" && diffOpen) { | |
| 2946 | − void navigate({ | |
| 2947 | − to: "/$environmentId/$threadId", | |
| 2948 | − params: { environmentId, threadId }, | |
| 2949 | − replace: true, | |
| 2950 | − search: (previous) => ({ ...stripDiffSearchParams(previous), diff: undefined }), | |
| 2951 | − }); | |
| 2952 | 2879 | } |
| 2953 | 2880 | }, |
| 2954 | − [ | |
| 2955 | − activeThreadRef, | |
| 2956 | − diffOpen, | |
| 2957 | − dismissPlanSidebarForCurrentTurn, | |
| 2958 | − environmentId, | |
| 2959 | − navigate, | |
| 2960 | − onDiffPanelOpen, | |
| 2961 | − planSidebarOpen, | |
| 2962 | − threadId, | |
| 2963 | − ], | |
| 2881 | + [activeThreadRef, diffOpen, dismissPlanSidebarForCurrentTurn, onDiffPanelOpen, planSidebarOpen], | |
| 2964 | 2882 | ); |
| 2965 | 2883 | const toggleRightPanel = useCallback(() => { |
| 2966 | 2884 | if (!activeThreadRef) return; |
| @@ -3006,26 +2924,14 @@ function ChatViewContent(props: ChatViewProps) { · 39 unmodified lines | ||
| 3006 | 2924 | } |
| 3007 | 2925 | } |
| 3008 | 2926 | } |
| 3009 | − if (diffOpen && surfaces.some((surface) => surface.kind === "diff")) { | |
| 3010 | − void navigate({ | |
| 3011 | − to: "/$environmentId/$threadId", | |
| 3012 | − params: { environmentId, threadId }, | |
| 3013 | − replace: true, | |
| 3014 | − search: (previous) => ({ ...stripDiffSearchParams(previous), diff: undefined }), | |
| 3015 | − }); | |
| 3016 | − } | |
| 3017 | 2927 | }, |
| 3018 | 2928 | [ |
| 3019 | 2929 | activeThreadRef, |
| 3020 | 2930 | activePreviewState.sessions, |
| 3021 | 2931 | closePreview, |
| 3022 | 2932 | closeTerminalMutation, |
| 3023 | − diffOpen, | |
| 3024 | 2933 | dismissPlanSidebarForCurrentTurn, |
| 3025 | − environmentId, | |
| 3026 | − navigate, | |
| 3027 | 2934 | storeCloseTerminal, |
| 3028 | − threadId, | |
| 3029 | 2935 | ], |
| 3030 | 2936 | ); |
| 3031 | 2937 | const syncActivePreviewSurface = useCallback(() => { |
| @@ -4631,25 +4537,12 @@ function ChatViewContent(props: ChatViewProps) { · 1599 unmodified lines | ||
| 4631 | 4537 | }, []); |
| 4632 | 4538 | const onOpenTurnDiff = useCallback( |
| 4633 | 4539 | (turnId: TurnId, filePath?: string) => { |
| 4634 | − if (!isServerThread) { | |
| 4635 | − return; | |
| 4636 | − } | |
| 4540 | + if (!isServerThread || !activeThreadRef) return; | |
| 4541 | + useDiffPanelStore.getState().selectTurn(activeThreadRef, turnId, filePath); | |
| 4542 | + useRightPanelStore.getState().open(activeThreadRef, "diff"); | |
| 4637 | 4543 | onDiffPanelOpen?.(); |
| 4638 | − void navigate({ | |
| 4639 | − to: "/$environmentId/$threadId", | |
| 4640 | − params: { | |
| 4641 | − environmentId, | |
| 4642 | − threadId, | |
| 4643 | − }, | |
| 4644 | − search: (previous) => { | |
| 4645 | − const rest = stripDiffSearchParams(previous); | |
| 4646 | − return filePath | |
| 4647 | − ? { ...rest, diff: "1", diffTurnId: turnId, diffFilePath: filePath } | |
| 4648 | − : { ...rest, diff: "1", diffTurnId: turnId }; | |
| 4649 | − }, | |
| 4650 | − }); | |
| 4651 | 4544 | }, |
| 4652 | − [environmentId, isServerThread, navigate, onDiffPanelOpen, threadId], | |
| 4545 | + [activeThreadRef, isServerThread, onDiffPanelOpen], | |
| 4653 | 4546 | ); |
| 4654 | 4547 | // Both the Map and the revert handler are read from refs at call-time so |
| 4655 | 4548 | // the callback reference is fully stable and never busts context identity. |
| @@ -1,35 +1,28 @@ | ||
| 1 | 1 | import { useAtomValue } from "@effect/atom-react"; |
| 2 | −import { Virtualizer } from "@pierre/diffs/react"; | |
| 3 | −import { useNavigate, useParams, useSearch } from "@tanstack/react-router"; | |
| 4 | −import { scopeThreadRef } from "@t3tools/client-runtime/environment"; | |
| 2 | +import { useParams } from "@tanstack/react-router"; | |
| 5 | 3 | import { |
| 6 | 4 | isAtomCommandInterrupted, |
| 7 | 5 | squashAtomCommandFailure, |
| 8 | 6 | } from "@t3tools/client-runtime/state/runtime"; |
| 9 | 7 | import type { ScopedThreadRef, TurnId } from "@t3tools/contracts"; |
| 10 | 8 | import { |
| 9 | + ArrowRightIcon, | |
| 10 | + CheckIcon, | |
| 11 | 11 | ChevronDownIcon, |
| 12 | − ChevronLeftIcon, | |
| 13 | 12 | ChevronRightIcon, |
| 14 | 13 | Columns2Icon, |
| 15 | 14 | PilcrowIcon, |
| 16 | 15 | Rows3Icon, |
| 16 | + SearchIcon, | |
| 17 | 17 | TextWrapIcon, |
| 18 | 18 | } from "lucide-react"; |
| 19 | −import { | |
| 20 | − type WheelEvent as ReactWheelEvent, | |
| 21 | − useCallback, | |
| 22 | − useEffect, | |
| 23 | − useMemo, | |
| 24 | − useRef, | |
| 25 | − useState, | |
| 26 | −} from "react"; | |
| 19 | +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; | |
| 27 | 20 | import { useOpenInPreferredEditor } from "../editorPreferences"; |
| 28 | 21 | import { type DraftId } from "../composerDraftStore"; |
| 29 | 22 | import { openDiffFilePrimaryAction } from "../diffFileActions"; |
| 30 | 23 | import { useCheckpointDiff } from "~/lib/checkpointDiffState"; |
| 31 | 24 | import { cn } from "~/lib/utils"; |
| 32 | −import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch"; | |
| 25 | +import { selectThreadDiffPanelSelection, useDiffPanelStore } from "../diffPanelStore"; | |
| 33 | 26 | import { useTheme } from "../hooks/useTheme"; |
| 34 | 27 | import { |
| 35 | 28 | buildFileDiffRenderKey, |
| @@ -40,19 +33,41 @@ import { · 4 unmodified lines | ||
| 40 | 33 | } from "../lib/diffRendering"; |
| 41 | 34 | import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries"; |
| 42 | 35 | import { useProject, useThread } from "../state/entities"; |
| 43 | −import { buildThreadRouteParams, resolveThreadRouteRef } from "../threadRoutes"; | |
| 36 | +import { resolveThreadRouteRef } from "../threadRoutes"; | |
| 44 | 37 | import { useSettings } from "../hooks/useSettings"; |
| 45 | 38 | import { formatShortTimestamp } from "../timestampFormat"; |
| 46 | 39 | import { DiffPanelLoadingState, DiffPanelShell, type DiffPanelMode } from "./DiffPanelShell"; |
| 47 | −import { AnnotatableFileDiff } from "./diffs/AnnotatableFileDiff"; | |
| 40 | +import { AnnotatableCodeView, type AnnotatableCodeViewHandle } from "./diffs/AnnotatableFileDiff"; | |
| 48 | 41 | import { ToggleGroup, Toggle } from "./ui/toggle-group"; |
| 42 | +import { Switch } from "./ui/switch"; | |
| 43 | +import { | |
| 44 | + Combobox, | |
| 45 | + ComboboxEmpty, | |
| 46 | + ComboboxInput, | |
| 47 | + ComboboxItem, | |
| 48 | + ComboboxList, | |
| 49 | + ComboboxPopup, | |
| 50 | + ComboboxTrigger, | |
| 51 | +} from "./ui/combobox"; | |
| 52 | +import { | |
| 53 | + DropdownMenu, | |
| 54 | + DropdownMenuContent, | |
| 55 | + DropdownMenuItem, | |
| 56 | + DropdownMenuSub, | |
| 57 | + DropdownMenuSubContent, | |
| 58 | + DropdownMenuSubTrigger, | |
| 59 | + DropdownMenuTrigger, | |
| 60 | +} from "./ui/menu"; | |
| 49 | 61 | import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip"; |
| 50 | 62 | import { useEnvironmentQuery } from "../state/query"; |
| 51 | 63 | import { serverEnvironment } from "../state/server"; |
| 64 | +import { reviewEnvironment } from "../state/review"; | |
| 52 | 65 | import { vcsEnvironment } from "../state/vcs"; |
| 66 | +import { buildBaseRefChoices, filterBaseRefChoices } from "../lib/baseRefChoices"; | |
| 53 | 67 | |
| 54 | 68 | type DiffRenderMode = "stacked" | "split"; |
| 55 | 69 | type DiffThemeType = "light" | "dark"; |
| 70 | +const AUTOMATIC_BASE_REF = "__automatic_base_ref__"; | |
| 56 | 71 | |
| 57 | 72 | interface CollapsedDiffFilesState { |
| 58 | 73 | readonly scopeKey: string | null; |
| @@ -167,27 +182,24 @@ interface DiffPanelProps { · 108 unmodified lines | ||
| 167 | 182 | export { DiffWorkerPoolProvider } from "./DiffWorkerPoolProvider"; |
| 168 | 183 | |
| 169 | 184 | export default function DiffPanel({ mode = "inline", composerDraftTarget }: DiffPanelProps) { |
| 170 | − const navigate = useNavigate(); | |
| 171 | 185 | const { resolvedTheme } = useTheme(); |
| 172 | 186 | const settings = useSettings(); |
| 173 | 187 | const [diffRenderMode, setDiffRenderMode] = useState<DiffRenderMode>("stacked"); |
| 174 | 188 | const [diffWordWrap, setDiffWordWrap] = useState(settings.diffWordWrap); |
| 175 | 189 | const [diffIgnoreWhitespace, setDiffIgnoreWhitespace] = useState(settings.diffIgnoreWhitespace); |
| 190 | + const [baseRefQuery, setBaseRefQuery] = useState(""); | |
| 176 | 191 | const [collapsedDiffFiles, setCollapsedDiffFiles] = useState<CollapsedDiffFilesState>(() => ({ |
| 177 | 192 | scopeKey: null, |
| 178 | 193 | fileKeys: EMPTY_COLLAPSED_DIFF_FILE_KEYS, |
| 179 | 194 | })); |
| 180 | − const patchViewportRef = useRef<HTMLDivElement>(null); | |
| 181 | − const turnStripRef = useRef<HTMLDivElement>(null); | |
| 182 | − const previousDiffOpenRef = useRef(false); | |
| 183 | − const [canScrollTurnStripLeft, setCanScrollTurnStripLeft] = useState(false); | |
| 184 | − const [canScrollTurnStripRight, setCanScrollTurnStripRight] = useState(false); | |
| 195 | + const codeViewRef = useRef<AnnotatableCodeViewHandle>(null); | |
| 185 | 196 | const routeThreadRef = useParams({ |
| 186 | 197 | strict: false, |
| 187 | 198 | select: (params) => resolveThreadRouteRef(params), |
| 188 | 199 | }); |
| 189 | − const diffSearch = useSearch({ strict: false, select: (search) => parseDiffRouteSearch(search) }); | |
| 190 | − const diffOpen = diffSearch.diff === "1"; | |
| 200 | + const diffSelection = useDiffPanelStore((state) => | |
| 201 | + selectThreadDiffPanelSelection(state.byThreadKey, routeThreadRef), | |
| 202 | + ); | |
| 191 | 203 | const activeThreadId = routeThreadRef?.threadId ?? null; |
| 192 | 204 | const activeThread = useThread(routeThreadRef); |
| 193 | 205 | const activeProjectId = activeThread?.projectId ?? null; |
| @@ -233,8 +245,20 @@ export default function DiffPanel({ mode = "inline", composerDraftTarget }: Diff · 39 unmodified lines | ||
| 233 | 245 | [inferredCheckpointTurnCountByTurnId, turnDiffSummaries], |
| 234 | 246 | ); |
| 235 | 247 | |
| 236 | − const selectedTurnId = diffSearch.diffTurnId ?? null; | |
| 237 | − const selectedFilePath = selectedTurnId !== null ? (diffSearch.diffFilePath ?? null) : null; | |
| 248 | + useEffect(() => { | |
| 249 | + if (!routeThreadRef || diffSelection.kind !== "turn") return; | |
| 250 | + useDiffPanelStore.getState().reconcileTurnSelection( | |
| 251 | + routeThreadRef, | |
| 252 | + orderedTurnDiffSummaries.map((summary) => summary.turnId), | |
| 253 | + ); | |
| 254 | + }, [diffSelection, orderedTurnDiffSummaries, routeThreadRef]); | |
| 255 | + | |
| 256 | + const selectedTurnId = diffSelection.kind === "turn" ? diffSelection.turnId : null; | |
| 257 | + const selectedGitScope = diffSelection.kind === "unstaged" ? "unstaged" : "branch"; | |
| 258 | + const selectedBaseRef = diffSelection.kind === "branch" ? diffSelection.baseRef : null; | |
| 259 | + const selectedFilePath = diffSelection.kind === "turn" ? diffSelection.filePath : null; | |
| 260 | + const selectedFileRevealRequestId = | |
| 261 | + diffSelection.kind === "turn" ? diffSelection.revealRequestId : 0; | |
| 238 | 262 | const selectedTurn = |
| 239 | 263 | selectedTurnId === null |
| 240 | 264 | ? undefined |
| @@ -243,7 +267,16 @@ export default function DiffPanel({ mode = "inline", composerDraftTarget }: Diff · 2 unmodified lines | ||
| 243 | 267 | const selectedCheckpointTurnCount = |
| 244 | 268 | selectedTurn && |
| 245 | 269 | (selectedTurn.checkpointTurnCount ?? inferredCheckpointTurnCountByTurnId[selectedTurn.turnId]); |
| 246 | − const reviewSectionId = selectedTurn ? `turn:${selectedTurn.turnId}` : "conversation"; | |
| 270 | + const latestTurn = orderedTurnDiffSummaries[0]; | |
| 271 | + const selectedScopeLabel = | |
| 272 | + selectedTurnId === null | |
| 273 | + ? selectedGitScope === "unstaged" | |
| 274 | + ? "Working tree" | |
| 275 | + : "Branch changes" | |
| 276 | + : selectedTurn?.turnId === latestTurn?.turnId | |
| 277 | + ? "Latest turn" | |
| 278 | + : `Turn ${selectedCheckpointTurnCount ?? "?"}`; | |
| 279 | + const reviewSectionId = selectedTurn ? `turn:${selectedTurn.turnId}` : selectedGitScope; | |
| 247 | 280 | const collapseScopeKey = routeThreadRef |
| 248 | 281 | ? `${routeThreadRef.environmentId}:${routeThreadRef.threadId}:${reviewSectionId}` |
| 249 | 282 | : null; |
| @@ -253,7 +286,9 @@ export default function DiffPanel({ mode = "inline", composerDraftTarget }: Diff · 3 unmodified lines | ||
| 253 | 286 | : EMPTY_COLLAPSED_DIFF_FILE_KEYS; |
| 254 | 287 | const reviewSectionTitle = selectedTurn |
| 255 | 288 | ? `Turn ${selectedCheckpointTurnCount ?? "?"}` |
| 256 | − : "All turns"; | |
| 289 | + : selectedGitScope === "unstaged" | |
| 290 | + ? "Working tree" | |
| 291 | + : "Branch changes"; | |
| 257 | 292 | const selectedCheckpointRange = useMemo( |
| 258 | 293 | () => |
| 259 | 294 | typeof selectedCheckpointTurnCount === "number" |
| @@ -264,62 +299,116 @@ export default function DiffPanel({ mode = "inline", composerDraftTarget }: Diff · 4 unmodified lines | ||
| 264 | 299 | : null, |
| 265 | 300 | [selectedCheckpointTurnCount], |
| 266 | 301 | ); |
| 267 | − const conversationCheckpointTurnCount = useMemo(() => { | |
| 268 | − const turnCounts: Array<number> = []; | |
| 269 | − for (const summary of orderedTurnDiffSummaries) { | |
| 270 | − const value = | |
| 271 | − summary.checkpointTurnCount ?? inferredCheckpointTurnCountByTurnId[summary.turnId]; | |
| 272 | − if (typeof value === "number") { | |
| 273 | − turnCounts.push(value); | |
| 274 | − } | |
| 275 | − } | |
| 276 | − if (turnCounts.length === 0) { | |
| 277 | − return undefined; | |
| 278 | − } | |
| 279 | − const latest = Math.max(...turnCounts); | |
| 280 | − return latest > 0 ? latest : undefined; | |
| 281 | − }, [inferredCheckpointTurnCountByTurnId, orderedTurnDiffSummaries]); | |
| 282 | − const conversationCheckpointRange = useMemo( | |
| 283 | − () => | |
| 284 | − !selectedTurn && typeof conversationCheckpointTurnCount === "number" | |
| 285 | − ? { | |
| 286 | − fromTurnCount: 0, | |
| 287 | − toTurnCount: conversationCheckpointTurnCount, | |
| 288 | − } | |
| 289 | − : null, | |
| 290 | − [conversationCheckpointTurnCount, selectedTurn], | |
| 291 | − ); | |
| 292 | − const activeCheckpointRange = selectedTurn | |
| 293 | − ? selectedCheckpointRange | |
| 294 | − : conversationCheckpointRange; | |
| 295 | − const conversationCacheScope = useMemo(() => { | |
| 296 | − if (selectedTurn || orderedTurnDiffSummaries.length === 0) { | |
| 297 | − return null; | |
| 298 | − } | |
| 299 | − return `conversation:${orderedTurnDiffSummaries.map((summary) => summary.turnId).join(",")}`; | |
| 300 | − }, [orderedTurnDiffSummaries, selectedTurn]); | |
| 301 | 302 | const activeCheckpointDiff = useCheckpointDiff( |
| 302 | 303 | { |
| 303 | 304 | environmentId: activeThread?.environmentId ?? null, |
| 304 | 305 | threadId: activeThreadId, |
| 305 | − fromTurnCount: activeCheckpointRange?.fromTurnCount ?? null, | |
| 306 | − toTurnCount: activeCheckpointRange?.toTurnCount ?? null, | |
| 306 | + fromTurnCount: selectedCheckpointRange?.fromTurnCount ?? null, | |
| 307 | + toTurnCount: selectedCheckpointRange?.toTurnCount ?? null, | |
| 307 | 308 | ignoreWhitespace: diffIgnoreWhitespace, |
| 308 | − cacheScope: selectedTurn ? `turn:${selectedTurn.turnId}` : conversationCacheScope, | |
| 309 | + cacheScope: selectedTurn ? `turn:${selectedTurn.turnId}` : null, | |
| 309 | 310 | }, |
| 310 | − { enabled: isGitRepo }, | |
| 311 | + { enabled: isGitRepo && selectedTurn !== undefined }, | |
| 311 | 312 | ); |
| 312 | − const selectedTurnCheckpointDiff = selectedTurn ? activeCheckpointDiff.data?.diff : undefined; | |
| 313 | − const conversationCheckpointDiff = selectedTurn ? undefined : activeCheckpointDiff.data?.diff; | |
| 314 | − const isLoadingCheckpointDiff = activeCheckpointDiff.isPending; | |
| 315 | − const checkpointDiffError = activeCheckpointDiff.error; | |
| 316 | − | |
| 317 | − const selectedPatch = selectedTurn ? selectedTurnCheckpointDiff : conversationCheckpointDiff; | |
| 313 | + const primaryBranchDiffPreview = useEnvironmentQuery( | |
| 314 | + selectedTurnId === null && activeThread && activeCwd | |
| 315 | + ? reviewEnvironment.diffPreview({ | |
| 316 | + environmentId: activeThread.environmentId, | |
| 317 | + input: { | |
| 318 | + cwd: activeCwd, | |
| 319 | + ...(selectedBaseRef ? { baseRef: selectedBaseRef } : {}), | |
| 320 | + ignoreWhitespace: diffIgnoreWhitespace, | |
| 321 | + }, | |
| 322 | + }) | |
| 323 | + : null, | |
| 324 | + ); | |
| 325 | + const shouldRetryBranchDiffAtEnvironmentCwd = | |
| 326 | + selectedTurnId === null && | |
| 327 | + primaryBranchDiffPreview.error?.includes("configured workspace root") === true && | |
| 328 | + serverConfig?.cwd !== undefined && | |
| 329 | + serverConfig.cwd !== activeCwd; | |
| 330 | + const fallbackBranchDiffPreview = useEnvironmentQuery( | |
| 331 | + shouldRetryBranchDiffAtEnvironmentCwd && activeThread && serverConfig | |
| 332 | + ? reviewEnvironment.diffPreview({ | |
| 333 | + environmentId: activeThread.environmentId, | |
| 334 | + input: { | |
| 335 | + cwd: serverConfig.cwd, | |
| 336 | + ...(selectedBaseRef ? { baseRef: selectedBaseRef } : {}), | |
| 337 | + ignoreWhitespace: diffIgnoreWhitespace, | |
| 338 | + }, | |
| 339 | + }) | |
| 340 | + : null, | |
| 341 | + ); | |
| 342 | + const branchDiffPreview = shouldRetryBranchDiffAtEnvironmentCwd | |
| 343 | + ? fallbackBranchDiffPreview | |
| 344 | + : primaryBranchDiffPreview; | |
| 345 | + const selectedGitSource = branchDiffPreview.data?.sources.find( | |
| 346 | + (source) => source.kind === (selectedGitScope === "unstaged" ? "working-tree" : "branch-range"), | |
| 347 | + ); | |
| 348 | + const localBranchRefs = useEnvironmentQuery( | |
| 349 | + selectedTurnId === null && | |
| 350 | + selectedGitScope === "branch" && | |
| 351 | + activeThread && | |
| 352 | + branchDiffPreview.data?.cwd | |
| 353 | + ? vcsEnvironment.listRefs({ | |
| 354 | + environmentId: activeThread.environmentId, | |
| 355 | + input: { | |
| 356 | + cwd: branchDiffPreview.data.cwd, | |
| 357 | + includeMatchingRemoteRefs: true, | |
| 358 | + refKind: "local", | |
| 359 | + ...(baseRefQuery.trim().length > 0 ? { query: baseRefQuery.trim() } : {}), | |
| 360 | + limit: 100, | |
| 361 | + }, | |
| 362 | + }) | |
| 363 | + : null, | |
| 364 | + ); | |
| 365 | + const remoteBranchRefs = useEnvironmentQuery( | |
| 366 | + selectedTurnId === null && | |
| 367 | + selectedGitScope === "branch" && | |
| 368 | + activeThread && | |
| 369 | + branchDiffPreview.data?.cwd | |
| 370 | + ? vcsEnvironment.listRefs({ | |
| 371 | + environmentId: activeThread.environmentId, | |
| 372 | + input: { | |
| 373 | + cwd: branchDiffPreview.data.cwd, | |
| 374 | + includeMatchingRemoteRefs: true, | |
| 375 | + refKind: "remote", | |
| 376 | + ...(baseRefQuery.trim().length > 0 ? { query: baseRefQuery.trim() } : {}), | |
| 377 | + limit: 100, | |
| 378 | + }, | |
| 379 | + }) | |
| 380 | + : null, | |
| 381 | + ); | |
| 382 | + const baseRefChoices = buildBaseRefChoices( | |
| 383 | + localBranchRefs.data?.refs.filter((ref) => ref.name !== selectedGitSource?.headRef) ?? [], | |
| 384 | + remoteBranchRefs.data?.refs ?? [], | |
| 385 | + ); | |
| 386 | + const matchingBaseRefChoices = filterBaseRefChoices(baseRefChoices, baseRefQuery); | |
| 387 | + const valueForBaseRefChoice = (choice: (typeof baseRefChoices)[number]) => | |
| 388 | + selectedBaseRef && selectedBaseRef === choice.remote?.name | |
| 389 | + ? selectedBaseRef | |
| 390 | + : (choice.local?.name ?? choice.remote?.name ?? choice.id); | |
| 391 | + const baseRefItems = [AUTOMATIC_BASE_REF, ...baseRefChoices.map(valueForBaseRefChoice)]; | |
| 392 | + const filteredBaseRefItems = [ | |
| 393 | + ...(baseRefQuery.trim().length === 0 ? [AUTOMATIC_BASE_REF] : []), | |
| 394 | + ...matchingBaseRefChoices.map(valueForBaseRefChoice), | |
| 395 | + ]; | |
| 396 | + const gitDiff = selectedGitSource?.diff; | |
| 397 | + | |
| 398 | + const selectedPatch = selectedTurn ? activeCheckpointDiff.data?.diff : gitDiff; | |
| 399 | + const isSelectedPatchTruncated = !selectedTurn && selectedGitSource?.truncated === true; | |
| 400 | + const isLoadingSelectedPatch = selectedTurn | |
| 401 | + ? activeCheckpointDiff.isPending | |
| 402 | + : branchDiffPreview.isPending; | |
| 403 | + const selectedPatchError = selectedTurn ? activeCheckpointDiff.error : branchDiffPreview.error; | |
| 318 | 404 | const hasResolvedPatch = typeof selectedPatch === "string"; |
| 319 | 405 | const hasNoNetChanges = hasResolvedPatch && selectedPatch.trim().length === 0; |
| 320 | 406 | const renderablePatch = useMemo( |
| 321 | − () => getRenderablePatch(selectedPatch, `diff-panel:${resolvedTheme}`), | |
| 322 | − [resolvedTheme, selectedPatch], | |
| 407 | + () => | |
| 408 | + getRenderablePatch(selectedPatch, `diff-panel:${resolvedTheme}`, { | |
| 409 | + compactPartialHunkOffsets: selectedTurnId === null, | |
| 410 | + }), | |
| 411 | + [resolvedTheme, selectedPatch, selectedTurnId], | |
| 323 | 412 | ); |
| 324 | 413 | const renderableFiles = useMemo(() => { |
| 325 | 414 | if (!renderablePatch || renderablePatch.kind !== "files") { |
| @@ -332,24 +421,26 @@ export default function DiffPanel({ mode = "inline", composerDraftTarget }: Diff · 6 unmodified lines | ||
| 332 | 421 | }), |
| 333 | 422 | ); |
| 334 | 423 | }, [renderablePatch]); |
| 424 | + const codeViewFiles = useMemo( | |
| 425 | + () => | |
| 426 | + renderableFiles.map((fileDiff) => { | |
| 427 | + const fileKey = buildFileDiffRenderKey(fileDiff); | |
| 428 | + return { | |
| 429 | + fileDiff, | |
| 430 | + filePath: resolveFileDiffPath(fileDiff), | |
| 431 | + fileKey, | |
| 432 | + collapsed: collapsedDiffFileKeys.has(fileKey), | |
| 433 | + }; | |
| 434 | + }), | |
| 435 | + [collapsedDiffFileKeys, renderableFiles], | |
| 436 | + ); | |
| 335 | 437 | |
| 336 | 438 | useEffect(() => { |
| 337 | − if (diffOpen && !previousDiffOpenRef.current) { | |
| 338 | − setDiffWordWrap(settings.diffWordWrap); | |
| 339 | − setDiffIgnoreWhitespace(settings.diffIgnoreWhitespace); | |
| 340 | − } | |
| 341 | − previousDiffOpenRef.current = diffOpen; | |
| 342 | − }, [diffOpen, settings.diffIgnoreWhitespace, settings.diffWordWrap]); | |
| 343 | − | |
| 344 | − useEffect(() => { | |
| 345 | − if (!selectedFilePath || !patchViewportRef.current) { | |
| 346 | − return; | |
| 347 | − } | |
| 348 | − const target = Array.from( | |
| 349 | − patchViewportRef.current.querySelectorAll<HTMLElement>("[data-diff-file-path]"), | |
| 350 | − ).find((element) => element.dataset.diffFilePath === selectedFilePath); | |
| 351 | − target?.scrollIntoView({ block: "nearest" }); | |
| 352 | − }, [selectedFilePath, renderableFiles]); | |
| 439 | + if (!selectedFilePath) return; | |
| 440 | + const file = codeViewFiles.find((candidate) => candidate.filePath === selectedFilePath); | |
| 441 | + if (!file) return; | |
| 442 | + codeViewRef.current?.scrollTo({ type: "item", id: file.fileKey, align: "start" }); | |
| 443 | + }, [codeViewFiles, selectedFilePath, selectedFileRevealRequestId]); | |
| 353 | 444 | |
| 354 | 445 | const openDiffFile = useCallback( |
| 355 | 446 | (filePath: string) => { |
| @@ -385,186 +476,190 @@ export default function DiffPanel({ mode = "inline", composerDraftTarget }: Diff · 29 unmodified lines | ||
| 385 | 476 | ); |
| 386 | 477 | |
| 387 | 478 | const selectTurn = (turnId: TurnId) => { |
| 388 | − if (!activeThread) return; | |
| 389 | − void navigate({ | |
| 390 | − to: "/$environmentId/$threadId", | |
| 391 | − params: buildThreadRouteParams(scopeThreadRef(activeThread.environmentId, activeThread.id)), | |
| 392 | − search: (previous) => { | |
| 393 | − const rest = stripDiffSearchParams(previous); | |
| 394 | − return { ...rest, diff: "1", diffTurnId: turnId }; | |
| 395 | − }, | |
| 396 | − }); | |
| 479 | + if (!routeThreadRef) return; | |
| 480 | + useDiffPanelStore.getState().selectTurn(routeThreadRef, turnId); | |
| 397 | 481 | }; |
| 398 | − const selectWholeConversation = () => { | |
| 399 | − if (!activeThread) return; | |
| 400 | − void navigate({ | |
| 401 | − to: "/$environmentId/$threadId", | |
| 402 | − params: buildThreadRouteParams(scopeThreadRef(activeThread.environmentId, activeThread.id)), | |
| 403 | − search: (previous) => { | |
| 404 | − const rest = stripDiffSearchParams(previous); | |
| 405 | − return { ...rest, diff: "1" }; | |
| 406 | − }, | |
| 407 | − }); | |
| 482 | + const selectGitScope = (scope: "branch" | "unstaged") => { | |
| 483 | + if (!routeThreadRef) return; | |
| 484 | + useDiffPanelStore.getState().selectGitScope(routeThreadRef, scope); | |
| 485 | + }; | |
| 486 | + const selectBranchBaseRef = (baseRef: string | null) => { | |
| 487 | + if (!routeThreadRef) return; | |
| 488 | + useDiffPanelStore.getState().selectBranchBaseRef(routeThreadRef, baseRef); | |
| 408 | 489 | }; |
| 409 | − const updateTurnStripScrollState = useCallback(() => { | |
| 410 | − const element = turnStripRef.current; | |
| 411 | − if (!element) { | |
| 412 | − setCanScrollTurnStripLeft(false); | |
| 413 | − setCanScrollTurnStripRight(false); | |
| 414 | − return; | |
| 415 | − } | |
| 416 | − | |
| 417 | − const maxScrollLeft = Math.max(0, element.scrollWidth - element.clientWidth); | |
| 418 | − setCanScrollTurnStripLeft(element.scrollLeft > 4); | |
| 419 | − setCanScrollTurnStripRight(element.scrollLeft < maxScrollLeft - 4); | |
| 420 | − }, []); | |
| 421 | − const scrollTurnStripBy = useCallback((offset: number) => { | |
| 422 | − const element = turnStripRef.current; | |
| 423 | − if (!element) return; | |
| 424 | − element.scrollBy({ left: offset, behavior: "smooth" }); | |
| 425 | − }, []); | |
| 426 | − const onTurnStripWheel = useCallback((event: ReactWheelEvent<HTMLDivElement>) => { | |
| 427 | − const element = turnStripRef.current; | |
| 428 | − if (!element) return; | |
| 429 | − if (element.scrollWidth <= element.clientWidth + 1) return; | |
| 430 | − if (Math.abs(event.deltaY) <= Math.abs(event.deltaX)) return; | |
| 431 | − | |
| 432 | − event.preventDefault(); | |
| 433 | − element.scrollBy({ left: event.deltaY, behavior: "auto" }); | |
| 434 | − }, []); | |
| 435 | − | |
| 436 | − useEffect(() => { | |
| 437 | − const element = turnStripRef.current; | |
| 438 | − if (!element) return; | |
| 439 | − | |
| 440 | − const frameId = window.requestAnimationFrame(() => updateTurnStripScrollState()); | |
| 441 | − const onScroll = () => updateTurnStripScrollState(); | |
| 442 | − | |
| 443 | − element.addEventListener("scroll", onScroll, { passive: true }); | |
| 444 | − | |
| 445 | − const resizeObserver = new ResizeObserver(() => updateTurnStripScrollState()); | |
| 446 | − resizeObserver.observe(element); | |
| 447 | − | |
| 448 | − return () => { | |
| 449 | − window.cancelAnimationFrame(frameId); | |
| 450 | − element.removeEventListener("scroll", onScroll); | |
| 451 | − resizeObserver.disconnect(); | |
| 452 | − }; | |
| 453 | − }, [updateTurnStripScrollState]); | |
| 454 | − | |
| 455 | − useEffect(() => { | |
| 456 | − const frameId = window.requestAnimationFrame(() => updateTurnStripScrollState()); | |
| 457 | − return () => { | |
| 458 | − window.cancelAnimationFrame(frameId); | |
| 459 | − }; | |
| 460 | − }, [orderedTurnDiffSummaries, selectedTurnId, updateTurnStripScrollState]); | |
| 461 | − | |
| 462 | − useEffect(() => { | |
| 463 | − const element = turnStripRef.current; | |
| 464 | − if (!element) return; | |
| 465 | − | |
| 466 | − const selectedChip = element.querySelector<HTMLElement>("[data-turn-chip-selected='true']"); | |
| 467 | − selectedChip?.scrollIntoView({ block: "nearest", inline: "nearest", behavior: "smooth" }); | |
| 468 | − }, [selectedTurn?.turnId, selectedTurnId]); | |
| 469 | 490 | |
| 470 | 491 | const headerRow = ( |
| 471 | 492 | <> |
| 472 | − <div className="relative min-w-0 flex-1 [-webkit-app-region:no-drag]"> | |
| 473 | − <button | |
| 474 | − type="button" | |
| 475 | − className={cn( | |
| 476 | − "absolute left-0 top-1/2 z-20 inline-flex size-6 -translate-y-1/2 items-center justify-center rounded-md border bg-background/90 text-muted-foreground transition-colors", | |
| 477 | − canScrollTurnStripLeft | |
| 478 | − ? "border-border/70 hover:border-border hover:text-foreground" | |
| 479 | − : "cursor-not-allowed border-border/40 text-muted-foreground/40", | |
| 480 | − )} | |
| 481 | − onClick={() => scrollTurnStripBy(-180)} | |
| 482 | − disabled={!canScrollTurnStripLeft} | |
| 483 | − aria-label="Scroll turn list left" | |
| 484 | − > | |
| 485 | − <ChevronLeftIcon className="size-3.5" /> | |
| 486 | − </button> | |
| 487 | − <button | |
| 488 | − type="button" | |
| 489 | − className={cn( | |
| 490 | − "absolute right-0 top-1/2 z-20 inline-flex size-6 -translate-y-1/2 items-center justify-center rounded-md border bg-background/90 text-muted-foreground transition-colors", | |
| 491 | − canScrollTurnStripRight | |
| 492 | − ? "border-border/70 hover:border-border hover:text-foreground" | |
| 493 | − : "cursor-not-allowed border-border/40 text-muted-foreground/40", | |
| 494 | − )} | |
| 495 | − onClick={() => scrollTurnStripBy(180)} | |
| 496 | − disabled={!canScrollTurnStripRight} | |
| 497 | − aria-label="Scroll turn list right" | |
| 498 | − > | |
| 499 | − <ChevronRightIcon className="size-3.5" /> | |
| 500 | − </button> | |
| 501 | − <div | |
| 502 | − ref={turnStripRef} | |
| 503 | − className="turn-chip-strip flex gap-1 overflow-x-auto px-8 py-0.5" | |
| 504 | − style={ | |
| 505 | − canScrollTurnStripLeft || canScrollTurnStripRight | |
| 506 | − ? { | |
| 507 | − maskImage: `linear-gradient(to right, ${canScrollTurnStripLeft ? "transparent 24px, black 72px" : "black"}, ${canScrollTurnStripRight ? "black calc(100% - 72px), transparent calc(100% - 24px)" : "black"})`, | |
| 508 | − } | |
| 509 | − : undefined | |
| 510 | − } | |
| 511 | − onWheel={onTurnStripWheel} | |
| 512 | − > | |
| 513 | − <button | |
| 514 | − type="button" | |
| 515 | − className="shrink-0 rounded-md" | |
| 516 | − onClick={selectWholeConversation} | |
| 517 | − data-turn-chip-selected={selectedTurnId === null} | |
| 493 | + <div className="flex min-w-0 flex-1 items-center gap-3 [-webkit-app-region:no-drag]"> | |
| 494 | + <DropdownMenu> | |
| 495 | + <DropdownMenuTrigger | |
| 496 | + className="inline-flex h-6 max-w-full items-center gap-1 rounded-md bg-muted/70 px-2 text-xs font-medium text-foreground outline-none transition-colors hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring" | |
| 497 | + aria-label={`Diff scope: ${selectedScopeLabel}`} | |
| 518 | 498 | > |
| 519 | − <div | |
| 520 | − className={cn( | |
| 521 | − "rounded-md border px-2 py-1 text-left transition-colors", | |
| 522 | − selectedTurnId === null | |
| 523 | − ? "border-border bg-accent text-accent-foreground" | |
| 524 | − : "border-border/70 bg-background/70 text-muted-foreground/80 hover:border-border hover:text-foreground/80", | |
| 499 | + <span className="truncate">{selectedScopeLabel}</span> | |
| 500 | + <ChevronDownIcon className="size-3.5 shrink-0 text-muted-foreground" /> | |
| 501 | + </DropdownMenuTrigger> | |
| 502 | + <DropdownMenuContent align="start" className="w-60"> | |
| 503 | + <DropdownMenuItem onClick={() => selectGitScope("unstaged")}> | |
| 504 | + <span>Working tree</span> | |
| 505 | + {selectedTurnId === null && selectedGitScope === "unstaged" && ( | |
| 506 | + <CheckIcon className="ml-auto" /> | |
| 507 | + )} | |
| 508 | + </DropdownMenuItem> | |
| 509 | + <DropdownMenuItem onClick={() => selectGitScope("branch")}> | |
| 510 | + <span>Branch changes</span> | |
| 511 | + {selectedTurnId === null && selectedGitScope === "branch" && ( | |
| 512 | + <CheckIcon className="ml-auto" /> | |
| 525 | 513 | )} |
| 514 | + </DropdownMenuItem> | |
| 515 | + <DropdownMenuItem | |
| 516 | + onClick={() => { | |
| 517 | + if (latestTurn) selectTurn(latestTurn.turnId); | |
| 518 | + }} | |
| 526 | 519 | > |
| 527 | − <div className="text-[10px] leading-tight font-medium">All turns</div> | |
| 528 | − </div> | |
| 529 | − </button> | |
| 530 | − {orderedTurnDiffSummaries.map((summary) => ( | |
| 531 | − <Tooltip key={summary.turnId}> | |
| 532 | − <TooltipTrigger | |
| 533 | − render={ | |
| 534 | − <button | |
| 535 | − type="button" | |
| 536 | − aria-label={summary.turnId} | |
| 537 | − className="shrink-0 rounded-md" | |
| 538 | − onClick={() => selectTurn(summary.turnId)} | |
| 539 | − data-turn-chip-selected={summary.turnId === selectedTurn?.turnId} | |
| 540 | − /> | |
| 541 | − } | |
| 520 | + <span>Latest turn</span> | |
| 521 | + {selectedTurnId !== null && selectedTurn?.turnId === latestTurn?.turnId && ( | |
| 522 | + <CheckIcon className="ml-auto" /> | |
| 523 | + )} | |
| 524 | + </DropdownMenuItem> | |
| 525 | + <DropdownMenuSub> | |
| 526 | + <DropdownMenuSubTrigger>Turn</DropdownMenuSubTrigger> | |
| 527 | + <DropdownMenuSubContent className="w-64"> | |
| 528 | + {orderedTurnDiffSummaries.map((summary) => { | |
| 529 | + const turnCount = | |
| 530 | + summary.checkpointTurnCount ?? | |
| 531 | + inferredCheckpointTurnCountByTurnId[summary.turnId] ?? | |
| 532 | + "?"; | |
| 533 | + return ( | |
| 534 | + <DropdownMenuItem | |
| 535 | + key={summary.turnId} | |
| 536 | + onClick={() => selectTurn(summary.turnId)} | |
| 537 | + > | |
| 538 | + <span>Turn {turnCount}</span> | |
| 539 | + <span className="ml-auto text-xs tabular-nums text-muted-foreground"> | |
| 540 | + {formatShortTimestamp(summary.completedAt, settings.timestampFormat)} | |
| 541 | + </span> | |
| 542 | + {summary.turnId === selectedTurn?.turnId && <CheckIcon className="ml-1" />} | |
| 543 | + </DropdownMenuItem> | |
| 544 | + ); | |
| 545 | + })} | |
| 546 | + </DropdownMenuSubContent> | |
| 547 | + </DropdownMenuSub> | |
| 548 | + </DropdownMenuContent> | |
| 549 | + </DropdownMenu> | |
| 550 | + {selectedTurnId === null && selectedGitScope === "branch" && selectedGitSource?.baseRef && ( | |
| 551 | + <div | |
| 552 | + className="flex min-w-0 max-w-full items-center gap-2 overflow-hidden text-xs text-muted-foreground" | |
| 553 | + title={`${selectedGitSource.headRef ?? "HEAD"} → ${selectedGitSource.baseRef}`} | |
| 554 | + aria-label={`Comparing ${selectedGitSource.headRef ?? "HEAD"} against ${selectedGitSource.baseRef}`} | |
| 555 | + > | |
| 556 | + <span className="min-w-0 max-w-48 truncate">{selectedGitSource.headRef ?? "HEAD"}</span> | |
| 557 | + <ArrowRightIcon className="size-3.5 shrink-0 opacity-70" /> | |
| 558 | + <Combobox | |
| 559 | + items={baseRefItems} | |
| 560 | + filteredItems={filteredBaseRefItems} | |
| 561 | + value={selectedBaseRef ?? AUTOMATIC_BASE_REF} | |
| 562 | + onOpenChange={(open) => { | |
| 563 | + if (!open) setBaseRefQuery(""); | |
| 564 | + }} | |
| 565 | + onValueChange={(value) => { | |
| 566 | + if (!value) return; | |
| 567 | + selectBranchBaseRef(value === AUTOMATIC_BASE_REF ? null : value); | |
| 568 | + }} | |
| 569 | + > | |
| 570 | + <ComboboxTrigger | |
| 571 | + className="inline-flex min-w-0 max-w-48 items-center gap-1 overflow-hidden rounded-md px-1.5 py-1 outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring" | |
| 572 | + aria-label={`Change comparison target. Currently ${selectedGitSource.baseRef}`} | |
| 573 | + > | |
| 574 | + <span className="min-w-0 truncate">{selectedGitSource.baseRef}</span> | |
| 575 | + <ChevronDownIcon className="size-3.5 shrink-0 opacity-70" /> | |
| 576 | + </ComboboxTrigger> | |
| 577 | + <ComboboxPopup | |
| 578 | + align="start" | |
| 579 | + className="w-72 min-w-0 max-w-[calc(100vw-1rem)] overflow-hidden [&>[data-slot=combobox-popup]]:min-w-0 [&>[data-slot=combobox-popup]]:overflow-hidden" | |
| 542 | 580 | > |
| 543 | − <div | |
| 544 | − className={cn( | |
| 545 | − "rounded-md border px-2 py-1 text-left transition-colors", | |
| 546 | − summary.turnId === selectedTurn?.turnId | |
| 547 | − ? "border-border bg-accent text-accent-foreground" | |
| 548 | − : "border-border/70 bg-background/70 text-muted-foreground/80 hover:border-border hover:text-foreground/80", | |
| 549 | − )} | |
| 550 | − > | |
| 551 | − <div className="flex items-center gap-1"> | |
| 552 | − <span className="text-[10px] leading-tight font-medium"> | |
| 553 | − Turn{" "} | |
| 554 | − {summary.checkpointTurnCount ?? | |
| 555 | − inferredCheckpointTurnCountByTurnId[summary.turnId] ?? | |
| 556 | − "?"} | |
| 557 | − </span> | |
| 558 | − <span className="text-[9px] leading-tight opacity-70 tabular-nums"> | |
| 559 | − {formatShortTimestamp(summary.completedAt, settings.timestampFormat)} | |
| 560 | − </span> | |
| 581 | + <div className="min-w-0 shrink-0 px-3 pt-2.5"> | |
| 582 | + <div className="relative -translate-y-px border-b border-border/70 pb-1.5 transition-colors focus-within:border-ring"> | |
| 583 | + <SearchIcon | |
| 584 | + aria-hidden="true" | |
| 585 | + className="pointer-events-none absolute top-1.5 left-0 size-4 shrink-0 text-muted-foreground/55" | |
| 586 | + /> | |
| 587 | + <ComboboxInput | |
| 588 | + className="[&_input]:h-6.5 [&_input]:ps-5 [&_input]:font-sans [&_input]:leading-6.5" | |
| 589 | + inputClassName="rounded-none bg-transparent text-sm" | |
| 590 | + placeholder="Search refs..." | |
| 591 | + showTrigger={false} | |
| 592 | + size="sm" | |
| 593 | + unstyled | |
| 594 | + value={baseRefQuery} | |
| 595 | + onChange={(event) => setBaseRefQuery(event.target.value)} | |
| 596 | + /> | |
| 561 | 597 | </div> |
| 562 | 598 | </div> |
| 563 | − </TooltipTrigger> | |
| 564 | − <TooltipPopup side="top">{summary.turnId}</TooltipPopup> | |
| 565 | − </Tooltip> | |
| 566 | − ))} | |
| 567 | − </div> | |
| 599 | + <div className="grid shrink-0 grid-cols-[1rem_minmax(0,1fr)] items-center gap-2 border-b border-border/70 ps-3 pe-6.5 pt-2 pb-1.5 font-medium text-[10px] text-muted-foreground uppercase tracking-wide"> | |
| 600 | + <span aria-hidden="true" /> | |
| 601 | + <div className="grid min-w-0 grid-cols-[minmax(0,1fr)_2rem] items-center"> | |
| 602 | + <span>Branch</span> | |
| 603 | + <span className="text-right">Remote</span> | |
| 604 | + </div> | |
| 605 | + </div> | |
| 606 | + <ComboboxEmpty>No matching refs.</ComboboxEmpty> | |
| 607 | + <ComboboxList className="max-h-64 min-w-0 overflow-x-hidden"> | |
| 608 | + <ComboboxItem | |
| 609 | + className="h-8 w-full min-w-0 grid-cols-[1rem_minmax(0,1fr)] py-0" | |
| 610 | + contentClassName="w-full min-w-0 overflow-hidden" | |
| 611 | + value={AUTOMATIC_BASE_REF} | |
| 612 | + > | |
| 613 | + <span className="block min-w-0 truncate">Automatic</span> | |
| 614 | + </ComboboxItem> | |
| 615 | + {baseRefChoices.map((choice) => { | |
| 616 | + const item = valueForBaseRefChoice(choice); | |
| 617 | + const hasBoth = choice.local !== null && choice.remote !== null; | |
| 618 | + const useRemote = choice.remote?.name === item; | |
| 619 | + return ( | |
| 620 | + <ComboboxItem | |
| 621 | + key={choice.id} | |
| 622 | + className="h-8 w-full min-w-0 grid-cols-[1rem_minmax(0,1fr)] py-0" | |
| 623 | + contentClassName="w-full min-w-0 overflow-hidden" | |
| 624 | + value={item} | |
| 625 | + > | |
| 626 | + <div className="grid w-full min-w-0 grid-cols-[minmax(0,1fr)_2rem] items-center overflow-hidden"> | |
| 627 | + <span className="block min-w-0 truncate pe-2">{choice.label}</span> | |
| 628 | + {hasBoth ? ( | |
| 629 | + <div | |
| 630 | + className="flex justify-end" | |
| 631 | + onClick={(event) => event.stopPropagation()} | |
| 632 | + onPointerDown={(event) => event.stopPropagation()} | |
| 633 | + > | |
| 634 | + <Switch | |
| 635 | + aria-label={`Use remote version of ${choice.label}`} | |
| 636 | + checked={useRemote} | |
| 637 | + className="[--thumb-size:--spacing(3)]" | |
| 638 | + onCheckedChange={(checked) => { | |
| 639 | + const nextRef = checked | |
| 640 | + ? choice.remote?.name | |
| 641 | + : choice.local?.name; | |
| 642 | + if (nextRef) selectBranchBaseRef(nextRef); | |
| 643 | + }} | |
| 644 | + /> | |
| 645 | + </div> | |
| 646 | + ) : choice.remote ? ( | |
| 647 | + <span | |
| 648 | + className="flex justify-end text-muted-foreground" | |
| 649 | + title="Remote only" | |
| 650 | + > | |
| 651 | + <CheckIcon aria-hidden="true" className="size-3" /> | |
| 652 | + </span> | |
| 653 | + ) : null} | |
| 654 | + </div> | |
| 655 | + </ComboboxItem> | |
| 656 | + ); | |
| 657 | + })} | |
| 658 | + </ComboboxList> | |
| 659 | + </ComboboxPopup> | |
| 660 | + </Combobox> | |
| 661 | + </div> | |
| 662 | + )} | |
| 568 | 663 | </div> |
| 569 | 664 | <div className="flex shrink-0 items-center gap-1 [-webkit-app-region:no-drag]"> |
| 570 | 665 | <ToggleGroup |
| @@ -644,24 +739,35 @@ export default function DiffPanel({ mode = "inline", composerDraftTarget }: Diff · 73 unmodified lines | ||
| 644 | 739 | <div className="flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70"> |
| 645 | 740 | Turn diffs are unavailable because this project is not a git repository. |
| 646 | 741 | </div> |
| 647 | − ) : orderedTurnDiffSummaries.length === 0 ? ( | |
| 742 | + ) : selectedTurnId !== null && orderedTurnDiffSummaries.length === 0 ? ( | |
| 648 | 743 | <div className="flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70"> |
| 649 | 744 | No completed turns yet. |
| 650 | 745 | </div> |
| 651 | 746 | ) : ( |
| 652 | 747 | <> |
| 653 | − <div | |
| 654 | − ref={patchViewportRef} | |
| 655 | − className="diff-panel-viewport min-h-0 min-w-0 flex-1 overflow-hidden" | |
| 656 | − > | |
| 657 | − {checkpointDiffError && !renderablePatch && ( | |
| 748 | + <div className="diff-panel-viewport flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden"> | |
| 749 | + {isSelectedPatchTruncated && ( | |
| 750 | + <p className="shrink-0 border-b border-border/70 bg-muted/40 px-3 py-1.5 text-[11px] text-muted-foreground"> | |
| 751 | + This diff was truncated because it exceeded the preview limit. The changes shown are | |
| 752 | + incomplete. | |
| 753 | + </p> | |
| 754 | + )} | |
| 755 | + {selectedPatchError && !renderablePatch && ( | |
| 658 | 756 | <div className="px-3"> |
| 659 | − <p className="mb-2 text-[11px] text-red-500/80">{checkpointDiffError}</p> | |
| 757 | + <p className="mb-2 text-[11px] text-red-500/80">{selectedPatchError}</p> | |
| 660 | 758 | </div> |
| 661 | 759 | )} |
| 662 | 760 | {!renderablePatch ? ( |
| 663 | − isLoadingCheckpointDiff ? ( | |
| 664 | − <DiffPanelLoadingState label="Loading checkpoint diff..." /> | |
| 761 | + isLoadingSelectedPatch ? ( | |
| 762 | + <DiffPanelLoadingState | |
| 763 | + label={ | |
| 764 | + selectedTurn | |
| 765 | + ? "Loading checkpoint diff..." | |
| 766 | + : selectedGitScope === "unstaged" | |
| 767 | + ? "Loading working tree diff..." | |
| 768 | + : "Loading branch diff..." | |
| 769 | + } | |
| 770 | + /> | |
| 665 | 771 | ) : ( |
| 666 | 772 | <div className="flex h-full items-center justify-center px-3 py-2 text-xs text-muted-foreground/70"> |
| 667 | 773 | <p> |
| @@ -672,88 +778,73 @@ export default function DiffPanel({ mode = "inline", composerDraftTarget }: Diff · 4 unmodified lines | ||
| 672 | 778 | </div> |
| 673 | 779 | ) |
| 674 | 780 | ) : renderablePatch.kind === "files" ? ( |
| 675 | − <Virtualizer | |
| 676 | − className="diff-render-surface h-full min-h-0 overflow-auto px-2 pb-2" | |
| 677 | − config={{ | |
| 678 | − overscrollSize: 600, | |
| 679 | − intersectionObserverMargin: 1200, | |
| 781 | + <div | |
| 782 | + className="min-h-0 flex-1" | |
| 783 | + onClickCapture={(event) => { | |
| 784 | + const composedPath = event.nativeEvent.composedPath?.() ?? []; | |
| 785 | + const title = composedPath.find( | |
| 786 | + (node): node is HTMLElement => | |
| 787 | + node instanceof HTMLElement && node.hasAttribute("data-title"), | |
| 788 | + ); | |
| 789 | + const filePath = title?.textContent?.trim(); | |
| 790 | + if (filePath) openDiffFile(filePath); | |
| 680 | 791 | }} |
| 681 | 792 | > |
| 682 | − {renderableFiles.map((fileDiff) => { | |
| 683 | − const filePath = resolveFileDiffPath(fileDiff); | |
| 684 | − const fileKey = buildFileDiffRenderKey(fileDiff); | |
| 685 | − const themedFileKey = `${fileKey}:${resolvedTheme}`; | |
| 686 | − const collapsed = collapsedDiffFileKeys.has(fileKey); | |
| 687 | − return ( | |
| 688 | − <div | |
| 689 | − key={themedFileKey} | |
| 690 | − data-diff-file-path={filePath} | |
| 691 | − className="diff-render-file group/diff-file mb-2 rounded-md first:mt-2 last:mb-0" | |
| 692 | − onClickCapture={(event) => { | |
| 693 | − const nativeEvent = event.nativeEvent as MouseEvent; | |
| 694 | − const composedPath = nativeEvent.composedPath?.() ?? []; | |
| 695 | − const clickedHeader = composedPath.some((node) => { | |
| 696 | − if (!(node instanceof Element)) return false; | |
| 697 | − return node.hasAttribute("data-title"); | |
| 698 | − }); | |
| 699 | − if (!clickedHeader) return; | |
| 700 | − openDiffFile(filePath); | |
| 701 | − }} | |
| 702 | − > | |
| 703 | − <AnnotatableFileDiff | |
| 704 | − fileDiff={fileDiff} | |
| 705 | − filePath={filePath} | |
| 706 | − sectionId={reviewSectionId} | |
| 707 | − sectionTitle={reviewSectionTitle} | |
| 708 | − composerDraftTarget={composerDraftTarget} | |
| 709 | − renderHeaderPrefix={() => ( | |
| 710 | − <Tooltip> | |
| 711 | − <TooltipTrigger | |
| 712 | − render={ | |
| 713 | − <button | |
| 714 | − type="button" | |
| 715 | − className={cn( | |
| 716 | − "inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-sm border-0 bg-transparent p-0 transition-colors hover:bg-foreground/10 focus-visible:outline-hidden", | |
| 717 | − getDiffCollapseIconClassName(fileDiff), | |
| 718 | − )} | |
| 719 | − aria-label={ | |
| 720 | − collapsed ? `Expand ${filePath}` : `Collapse ${filePath}` | |
| 721 | − } | |
| 722 | − aria-expanded={!collapsed} | |
| 723 | − onClick={(event) => { | |
| 724 | − event.stopPropagation(); | |
| 725 | − toggleDiffFileCollapsed(fileKey); | |
| 726 | − }} | |
| 727 | − /> | |
| 728 | − } | |
| 729 | − > | |
| 730 | − {collapsed ? ( | |
| 731 | − <ChevronRightIcon className="size-4" /> | |
| 732 | − ) : ( | |
| 733 | − <ChevronDownIcon className="size-4" /> | |
| 793 | + <AnnotatableCodeView | |
| 794 | + viewerRef={codeViewRef} | |
| 795 | + key={collapseScopeKey ?? reviewSectionId} | |
| 796 | + className="diff-render-surface h-full min-h-0 overflow-auto" | |
| 797 | + files={codeViewFiles} | |
| 798 | + sectionId={reviewSectionId} | |
| 799 | + sectionTitle={reviewSectionTitle} | |
| 800 | + composerDraftTarget={composerDraftTarget} | |
| 801 | + renderHeaderPrefix={(fileDiff, fileKey, collapsed) => { | |
| 802 | + const filePath = resolveFileDiffPath(fileDiff); | |
| 803 | + return ( | |
| 804 | + <Tooltip> | |
| 805 | + <TooltipTrigger | |
| 806 | + render={ | |
| 807 | + <button | |
| 808 | + type="button" | |
| 809 | + className={cn( | |
| 810 | + "inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-sm border-0 bg-transparent p-0 transition-colors hover:bg-foreground/10 focus-visible:outline-hidden", | |
| 811 | + getDiffCollapseIconClassName(fileDiff), | |
| 734 | 812 | )} |
| 735 | − </TooltipTrigger> | |
| 736 | − <TooltipPopup side="top"> | |
| 737 | − {collapsed ? "Expand diff" : "Collapse diff"} | |
| 738 | − </TooltipPopup> | |
| 739 | − </Tooltip> | |
| 740 | − )} | |
| 741 | − options={{ | |
| 742 | − collapsed, | |
| 743 | − diffStyle: diffRenderMode === "split" ? "split" : "unified", | |
| 744 | − lineDiffType: "none", | |
| 745 | − overflow: diffWordWrap ? "wrap" : "scroll", | |
| 746 | − theme: resolveDiffThemeName(resolvedTheme), | |
| 747 | − themeType: resolvedTheme as DiffThemeType, | |
| 748 | − unsafeCSS: DIFF_PANEL_UNSAFE_CSS, | |
| 749 | − }} | |
| 750 | − /> | |
| 751 | − </div> | |
| 752 | − ); | |
| 753 | − })} | |
| 754 | − </Virtualizer> | |
| 813 | + aria-label={collapsed ? `Expand ${filePath}` : `Collapse ${filePath}`} | |
| 814 | + aria-expanded={!collapsed} | |
| 815 | + onClick={(event) => { | |
| 816 | + event.stopPropagation(); | |
| 817 | + toggleDiffFileCollapsed(fileKey); | |
| 818 | + }} | |
| 819 | + /> | |
| 820 | + } | |
| 821 | + > | |
| 822 | + {collapsed ? ( | |
| 823 | + <ChevronRightIcon className="size-4" /> | |
| 824 | + ) : ( | |
| 825 | + <ChevronDownIcon className="size-4" /> | |
| 826 | + )} | |
| 827 | + </TooltipTrigger> | |
| 828 | + <TooltipPopup side="top"> | |
| 829 | + {collapsed ? "Expand diff" : "Collapse diff"} | |
| 830 | + </TooltipPopup> | |
| 831 | + </Tooltip> | |
| 832 | + ); | |
| 833 | + }} | |
| 834 | + options={{ | |
| 835 | + diffStyle: diffRenderMode === "split" ? "split" : "unified", | |
| 836 | + lineDiffType: "none", | |
| 837 | + overflow: diffWordWrap ? "wrap" : "scroll", | |
| 838 | + theme: resolveDiffThemeName(resolvedTheme), | |
| 839 | + themeType: resolvedTheme as DiffThemeType, | |
| 840 | + unsafeCSS: DIFF_PANEL_UNSAFE_CSS, | |
| 841 | + stickyHeaders: true, | |
| 842 | + layout: { paddingTop: 8, paddingBottom: 8, gap: 8 }, | |
| 843 | + }} | |
| 844 | + /> | |
| 845 | + </div> | |
| 755 | 846 | ) : ( |
| 756 | − <div className="h-full overflow-auto p-2"> | |
| 847 | + <div className="min-h-0 flex-1 overflow-auto p-2"> | |
| 757 | 848 | <div className="space-y-2"> |
| 758 | 849 | <p className="text-[11px] text-muted-foreground/75">{renderablePatch.reason}</p> |
| 759 | 850 | <pre |
| @@ -48,14 +48,8 @@ export function DiffPanelShell(props: { · 47 unmodified lines | ||
| 48 | 48 | export function DiffPanelHeaderSkeleton() { |
| 49 | 49 | return ( |
| 50 | 50 | <> |
| 51 | − <div className="relative min-w-0 flex-1"> | |
| 52 | − <Skeleton className="absolute left-0 top-1/2 size-6 -translate-y-1/2 rounded-md border border-border/50" /> | |
| 53 | − <Skeleton className="absolute right-0 top-1/2 size-6 -translate-y-1/2 rounded-md border border-border/50" /> | |
| 54 | − <div className="flex gap-1 overflow-hidden px-8 py-0.5"> | |
| 55 | − <Skeleton className="h-6 w-16 shrink-0 rounded-md" /> | |
| 56 | − <Skeleton className="h-6 w-24 shrink-0 rounded-md" /> | |
| 57 | − <Skeleton className="h-6 w-24 shrink-0 rounded-md max-sm:hidden" /> | |
| 58 | − </div> | |
| 51 | + <div className="min-w-0 flex-1"> | |
| 52 | + <Skeleton className="h-8 w-32 rounded-lg" /> | |
| 59 | 53 | </div> |
| 60 | 54 | <div className="flex shrink-0 gap-1"> |
| 61 | 55 | <Skeleton className="size-7 rounded-md" /> |
| @@ -1,14 +1,23 @@ | ||
| 1 | 1 | import type { |
| 2 | 2 | AnnotationSide, |
| 3 | + CodeViewDiffItem, | |
| 4 | + CodeViewItem, | |
| 3 | 5 | DiffLineAnnotation, |
| 4 | 6 | FileDiffMetadata, |
| 5 | 7 | SelectedLineRange, |
| 6 | 8 | } from "@pierre/diffs"; |
| 7 | −import { FileDiff, type FileDiffProps } from "@pierre/diffs/react"; | |
| 9 | +import { | |
| 10 | + CodeView, | |
| 11 | + type CodeViewHandle, | |
| 12 | + type CodeViewProps, | |
| 13 | + FileDiff, | |
| 14 | + type FileDiffProps, | |
| 15 | +} from "@pierre/diffs/react"; | |
| 8 | 16 | import type { ScopedThreadRef } from "@t3tools/contracts"; |
| 9 | −import { useCallback, useMemo, useState, type ReactNode } from "react"; | |
| 17 | +import { useCallback, useMemo, useState, type ReactNode, type Ref } from "react"; | |
| 10 | 18 | |
| 11 | 19 | import { type DraftId, useComposerDraftStore } from "~/composerDraftStore"; |
| 20 | +import { fnv1a32 } from "~/lib/diffRendering"; | |
| 12 | 21 | import { |
| 13 | 22 | buildDiffReviewComment, |
| 14 | 23 | restoreDiffReviewCommentRange, |
| @@ -31,6 +40,7 @@ interface DiffCommentAnnotationGroup { · 16 unmodified lines | ||
| 31 | 40 | } |
| 32 | 41 | |
| 33 | 42 | type DiffCommentLineAnnotation = DiffLineAnnotation<DiffCommentAnnotationGroup>; |
| 43 | +export type AnnotatableCodeViewHandle = CodeViewHandle<DiffCommentAnnotationGroup>; | |
| 34 | 44 | const EMPTY_REVIEW_COMMENTS: ReadonlyArray<ReviewCommentContext> = []; |
| 35 | 45 | |
| 36 | 46 | function annotationSide(range: SelectedLineRange): AnnotationSide { |
| @@ -237,3 +247,200 @@ export function AnnotatableFileDiff({ · 200 unmodified lines | ||
| 237 | 247 | /> |
| 238 | 248 | ); |
| 239 | 249 | } |
| 250 | + | |
| 251 | +interface AnnotatableCodeViewProps { | |
| 252 | + files: ReadonlyArray<{ | |
| 253 | + fileDiff: FileDiffMetadata; | |
| 254 | + filePath: string; | |
| 255 | + fileKey: string; | |
| 256 | + collapsed: boolean; | |
| 257 | + }>; | |
| 258 | + sectionId: string; | |
| 259 | + sectionTitle: string; | |
| 260 | + composerDraftTarget: ScopedThreadRef | DraftId; | |
| 261 | + options: NonNullable<CodeViewProps<DiffCommentAnnotationGroup>["options"]>; | |
| 262 | + viewerRef?: Ref<AnnotatableCodeViewHandle>; | |
| 263 | + className?: string; | |
| 264 | + renderHeaderPrefix: ( | |
| 265 | + fileDiff: FileDiffMetadata, | |
| 266 | + fileKey: string, | |
| 267 | + collapsed: boolean, | |
| 268 | + ) => ReactNode; | |
| 269 | +} | |
| 270 | + | |
| 271 | +interface DiffSelectionContext { | |
| 272 | + item: CodeViewItem<DiffCommentAnnotationGroup>; | |
| 273 | +} | |
| 274 | + | |
| 275 | +export function AnnotatableCodeView({ | |
| 276 | + files, | |
| 277 | + sectionId, | |
| 278 | + sectionTitle, | |
| 279 | + composerDraftTarget, | |
| 280 | + options, | |
| 281 | + viewerRef, | |
| 282 | + className, | |
| 283 | + renderHeaderPrefix, | |
| 284 | +}: AnnotatableCodeViewProps) { | |
| 285 | + const addReviewComment = useComposerDraftStore((store) => store.addReviewComment); | |
| 286 | + const removeReviewComment = useComposerDraftStore((store) => store.removeReviewComment); | |
| 287 | + const reviewComments = useComposerDraftStore( | |
| 288 | + (store) => store.getComposerDraft(composerDraftTarget)?.reviewComments ?? EMPTY_REVIEW_COMMENTS, | |
| 289 | + ); | |
| 290 | + const [selectedLines, setSelectedLines] = useState<{ | |
| 291 | + id: string; | |
| 292 | + range: SelectedLineRange; | |
| 293 | + } | null>(null); | |
| 294 | + const [draft, setDraft] = useState<{ | |
| 295 | + fileKey: string; | |
| 296 | + annotation: DiffCommentLineAnnotation; | |
| 297 | + } | null>(null); | |
| 298 | + | |
| 299 | + const filesByKey = useMemo(() => new Map(files.map((file) => [file.fileKey, file])), [files]); | |
| 300 | + const items = useMemo<CodeViewDiffItem<DiffCommentAnnotationGroup>[]>( | |
| 301 | + () => | |
| 302 | + files.map(({ fileDiff, filePath, fileKey, collapsed }) => { | |
| 303 | + const persisted = reviewComments | |
| 304 | + .filter( | |
| 305 | + (comment) => | |
| 306 | + comment.sectionId === sectionId && | |
| 307 | + comment.filePath === filePath && | |
| 308 | + (comment.fenceLanguage ?? "diff") === "diff", | |
| 309 | + ) | |
| 310 | + .reduce<DiffCommentLineAnnotation[]>((annotations, comment) => { | |
| 311 | + const range = restoreDiffReviewCommentRange(fileDiff, comment); | |
| 312 | + if (!range) return annotations; | |
| 313 | + return appendAnnotationEntry(annotations, range, { | |
| 314 | + id: comment.id, | |
| 315 | + kind: "comment", | |
| 316 | + range, | |
| 317 | + rangeLabel: comment.rangeLabel, | |
| 318 | + text: comment.text, | |
| 319 | + }); | |
| 320 | + }, []); | |
| 321 | + const annotations = | |
| 322 | + draft?.fileKey === fileKey ? [...persisted, draft.annotation] : persisted; | |
| 323 | + return { | |
| 324 | + id: fileKey, | |
| 325 | + type: "diff", | |
| 326 | + fileDiff, | |
| 327 | + annotations, | |
| 328 | + collapsed, | |
| 329 | + version: fnv1a32( | |
| 330 | + `${collapsed ? "1" : "0"}:${annotations | |
| 331 | + .flatMap((annotation) => | |
| 332 | + annotation.metadata.entries.map( | |
| 333 | + (entry) => `${entry.id}:${entry.rangeLabel}:${entry.text}`, | |
| 334 | + ), | |
| 335 | + ) | |
| 336 | + .join(":")}`, | |
| 337 | + ), | |
| 338 | + }; | |
| 339 | + }), | |
| 340 | + [draft, files, reviewComments, sectionId], | |
| 341 | + ); | |
| 342 | + | |
| 343 | + const removeEntry = useCallback( | |
| 344 | + (entryId: string) => { | |
| 345 | + setSelectedLines(null); | |
| 346 | + if (draft?.annotation.metadata.entries.some((entry) => entry.id === entryId)) { | |
| 347 | + setDraft(null); | |
| 348 | + } else { | |
| 349 | + removeReviewComment(composerDraftTarget, entryId); | |
| 350 | + } | |
| 351 | + }, | |
| 352 | + [composerDraftTarget, draft, removeReviewComment], | |
| 353 | + ); | |
| 354 | + | |
| 355 | + const submitEntry = useCallback( | |
| 356 | + (entryId: string, text: string) => { | |
| 357 | + const entry = draft?.annotation.metadata.entries.find( | |
| 358 | + (candidate) => candidate.id === entryId, | |
| 359 | + ); | |
| 360 | + const file = draft ? filesByKey.get(draft.fileKey) : undefined; | |
| 361 | + if (!entry || !file) return; | |
| 362 | + const comment = buildDiffReviewComment({ | |
| 363 | + id: entry.id, | |
| 364 | + sectionId, | |
| 365 | + sectionTitle, | |
| 366 | + filePath: file.filePath, | |
| 367 | + fileDiff: file.fileDiff, | |
| 368 | + range: entry.range, | |
| 369 | + text, | |
| 370 | + }); | |
| 371 | + if (comment) addReviewComment(composerDraftTarget, comment); | |
| 372 | + setSelectedLines(null); | |
| 373 | + setDraft(null); | |
| 374 | + }, | |
| 375 | + [addReviewComment, composerDraftTarget, draft, filesByKey, sectionId, sectionTitle], | |
| 376 | + ); | |
| 377 | + | |
| 378 | + const beginComment = useCallback( | |
| 379 | + (range: SelectedLineRange | null, context: DiffSelectionContext) => { | |
| 380 | + if (!range) return; | |
| 381 | + const item = context.item; | |
| 382 | + if (item.type !== "diff") return; | |
| 383 | + const file = filesByKey.get(item.id); | |
| 384 | + if (!file) return; | |
| 385 | + const id = nextFileCommentId(); | |
| 386 | + const comment = buildDiffReviewComment({ | |
| 387 | + id, | |
| 388 | + sectionId, | |
| 389 | + sectionTitle, | |
| 390 | + filePath: file.filePath, | |
| 391 | + fileDiff: file.fileDiff, | |
| 392 | + range, | |
| 393 | + text: "", | |
| 394 | + }); | |
| 395 | + if (!comment) return; | |
| 396 | + setDraft({ | |
| 397 | + fileKey: item.id, | |
| 398 | + annotation: { | |
| 399 | + side: annotationSide(range), | |
| 400 | + lineNumber: range.end, | |
| 401 | + metadata: { | |
| 402 | + entries: [{ id, kind: "draft", range, rangeLabel: comment.rangeLabel, text: "" }], | |
| 403 | + }, | |
| 404 | + }, | |
| 405 | + }); | |
| 406 | + }, | |
| 407 | + [filesByKey, sectionId, sectionTitle], | |
| 408 | + ); | |
| 409 | + | |
| 410 | + const hasOpenComment = draft !== null; | |
| 411 | + return ( | |
| 412 | + <CodeView<DiffCommentAnnotationGroup> | |
| 413 | + {...(viewerRef ? { ref: viewerRef } : {})} | |
| 414 | + {...(className ? { className } : {})} | |
| 415 | + items={items} | |
| 416 | + selectedLines={selectedLines} | |
| 417 | + onSelectedLinesChange={setSelectedLines} | |
| 418 | + options={{ | |
| 419 | + ...options, | |
| 420 | + enableGutterUtility: !hasOpenComment, | |
| 421 | + enableLineSelection: !hasOpenComment, | |
| 422 | + onLineSelectionEnd: beginComment, | |
| 423 | + }} | |
| 424 | + renderHeaderPrefix={(item) => | |
| 425 | + item.type === "diff" | |
| 426 | + ? renderHeaderPrefix(item.fileDiff, item.id, item.collapsed === true) | |
| 427 | + : null | |
| 428 | + } | |
| 429 | + renderAnnotation={(annotation) => ( | |
| 430 | + <div className="py-1"> | |
| 431 | + {annotation.metadata.entries.map((entry) => ( | |
| 432 | + <LocalCommentAnnotation | |
| 433 | + key={entry.id} | |
| 434 | + kind={entry.kind} | |
| 435 | + rangeLabel={entry.rangeLabel} | |
| 436 | + text={entry.text} | |
| 437 | + onCancel={() => removeEntry(entry.id)} | |
| 438 | + onComment={(text) => submitEntry(entry.id, text)} | |
| 439 | + onDelete={() => removeEntry(entry.id)} | |
| 440 | + /> | |
| 441 | + ))} | |
| 442 | + </div> | |
| 443 | + )} | |
| 444 | + /> | |
| 445 | + ); | |
| 446 | +} | |
| @@ -3,11 +3,11 @@ import { describe, expect, it } from "vite-plus/test"; · 2 unmodified lines | ||
| 3 | 3 | import { observeAutomationOwnerConnectedGeneration } from "./PreviewAutomationOwner"; |
| 4 | 4 | |
| 5 | 5 | describe("observeAutomationOwnerConnectedGeneration", () => { |
| 6 | − it("re-reports ownership only after a later transport generation connects", () => { | |
| 6 | + it("reports ownership when the initial transport generation connects", () => { | |
| 7 | 7 | const initial = observeAutomationOwnerConnectedGeneration(null, 1); |
| 8 | 8 | expect(initial).toEqual({ |
| 9 | 9 | nextGeneration: 1, |
| 10 | − shouldReport: false, | |
| 10 | + shouldReport: true, | |
| 11 | 11 | }); |
| 12 | 12 | |
| 13 | 13 | const disconnected = observeAutomationOwnerConnectedGeneration(initial.nextGeneration, null); |
| @@ -1,15 +1,16 @@ | ||
| 1 | 1 | "use client"; |
| 2 | 2 | |
| 3 | +import { useAtomValue } from "@effect/atom-react"; | |
| 3 | 4 | import { squashAtomCommandFailure } from "@t3tools/client-runtime/state/runtime"; |
| 4 | 5 | import type { |
| 5 | 6 | PreviewAutomationNavigateInput, |
| 6 | 7 | PreviewAutomationOpenInput, |
| 7 | 8 | PreviewAutomationRequest, |
| 8 | − PreviewAutomationResponse, | |
| 9 | + PreviewAutomationOwner as PreviewAutomationOwnerState, | |
| 9 | 10 | PreviewAutomationStatus, |
| 10 | 11 | ScopedThreadRef, |
| 11 | 12 | } from "@t3tools/contracts"; |
| 12 | −import { useCallback, useEffect, useId, useRef } from "react"; | |
| 13 | +import { useCallback, useEffect, useEffectEvent, useId, useMemo, useRef, useState } from "react"; | |
| 13 | 14 | |
| 14 | 15 | import { |
| 15 | 16 | applyPreviewServerSnapshot, |
| @@ -20,11 +21,14 @@ import { useRightPanelStore } from "~/rightPanelStore"; · 4 unmodified lines | ||
| 20 | 21 | import { resolveBrowserNavigationTarget } from "~/browser/browserTargetResolver"; |
| 21 | 22 | import { startBrowserRecording, stopBrowserRecording } from "~/browser/browserRecording"; |
| 22 | 23 | import { previewEnvironment } from "~/state/preview"; |
| 23 | −import { useEnvironmentQuery } from "~/state/query"; | |
| 24 | 24 | import { useEnvironmentConnectionState } from "~/state/environments"; |
| 25 | 25 | import { useAtomCommand } from "~/state/use-atom-command"; |
| 26 | 26 | |
| 27 | 27 | import { previewBridge } from "./previewBridge"; |
| 28 | +import { | |
| 29 | + createLatestPreviewAutomationRequestHandler, | |
| 30 | + createPreviewAutomationRequestConsumerAtom, | |
| 31 | +} from "./previewAutomationRequestConsumer"; | |
| 28 | 32 | |
| 29 | 33 | export function observeAutomationOwnerConnectedGeneration( |
| 30 | 34 | previousGeneration: number | null, |
| @@ -41,7 +45,7 @@ export function observeAutomationOwnerConnectedGeneration( · 10 unmodified lines | ||
| 41 | 45 | } |
| 42 | 46 | return { |
| 43 | 47 | nextGeneration: connectedGeneration, |
| 44 | − shouldReport: previousGeneration !== null && previousGeneration !== connectedGeneration, | |
| 48 | + shouldReport: previousGeneration !== connectedGeneration, | |
| 45 | 49 | }; |
| 46 | 50 | } |
| 47 | 51 | |
| @@ -109,24 +113,10 @@ const currentStatus = async ( · 61 unmodified lines | ||
| 109 | 113 | }; |
| 110 | 114 | }; |
| 111 | 115 | |
| 112 | −const serializeError = (error: unknown): NonNullable<PreviewAutomationResponse["error"]> => { | |
| 113 | − if (error instanceof Error) { | |
| 114 | − const detail = | |
| 115 | − "detail" in error && (error as { detail?: unknown }).detail !== undefined | |
| 116 | − ? (error as { detail?: unknown }).detail | |
| 117 | − : undefined; | |
| 118 | − return { | |
| 119 | − _tag: error.name.startsWith("PreviewAutomation") | |
| 120 | − ? error.name | |
| 121 | − : "PreviewAutomationExecutionError", | |
| 122 | − message: error.message, | |
| 123 | − ...(detail === undefined ? {} : { detail }), | |
| 124 | − }; | |
| 125 | − } | |
| 126 | − return { | |
| 127 | − _tag: "PreviewAutomationExecutionError", | |
| 128 | − message: String(error), | |
| 129 | − }; | |
| 116 | +const previewTabNotFoundError = (): Error => { | |
| 117 | + const error = new Error("Preview tab is not initialized."); | |
| 118 | + error.name = "PreviewAutomationTabNotFoundError"; | |
| 119 | + return error; | |
| 130 | 120 | }; |
| 131 | 121 | |
| 132 | 122 | export function PreviewAutomationOwner(props: { |
| @@ -135,12 +125,22 @@ export function PreviewAutomationOwner(props: { · 2 unmodified lines | ||
| 135 | 125 | }) { |
| 136 | 126 | const { threadRef, visible } = props; |
| 137 | 127 | const automationClientId = useId(); |
| 138 | − const automationRequests = useEnvironmentQuery( | |
| 139 | − previewEnvironment.automationRequests({ | |
| 128 | + const initialAutomationOwner = useMemo<PreviewAutomationOwnerState>( | |
| 129 | + () => ({ | |
| 130 | + clientId: automationClientId, | |
| 140 | 131 | environmentId: threadRef.environmentId, |
| 141 | − input: { clientId: automationClientId }, | |
| 132 | + threadId: threadRef.threadId, | |
| 133 | + tabId: null, | |
| 134 | + visible: false, | |
| 135 | + supportsAutomation: Boolean(previewBridge?.automation), | |
| 136 | + focusedAt: new Date().toISOString(), | |
| 142 | 137 | }), |
| 138 | + [automationClientId, threadRef.environmentId, threadRef.threadId], | |
| 143 | 139 | ); |
| 140 | + const automationRequestsAtom = previewEnvironment.automationRequests({ | |
| 141 | + environmentId: threadRef.environmentId, | |
| 142 | + input: initialAutomationOwner, | |
| 143 | + }); | |
| 144 | 144 | const connectionState = useEnvironmentConnectionState(threadRef.environmentId).data; |
| 145 | 145 | const connectedGeneration = |
| 146 | 146 | connectionState?.phase === "connected" ? connectionState.generation : null; |
| @@ -159,13 +159,24 @@ export function PreviewAutomationOwner(props: { · 12 unmodified lines | ||
| 159 | 159 | previewEnvironment.clearAutomationOwner, |
| 160 | 160 | "preview automation owner clear", |
| 161 | 161 | ); |
| 162 | − const ownerStateRef = useRef({ threadRef, visible }); | |
| 163 | 162 | const connectedGenerationRef = useRef<number | null>(null); |
| 164 | − const handlerRef = useRef<(request: PreviewAutomationRequest) => Promise<unknown>>( | |
| 165 | − async () => undefined, | |
| 166 | − ); | |
| 163 | + const reportCurrentAutomationOwner = useEffectEvent(() => { | |
| 164 | + const state = readThreadPreviewState(threadRef); | |
| 165 | + return reportAutomationOwner({ | |
| 166 | + environmentId: threadRef.environmentId, | |
| 167 | + input: { | |
| 168 | + clientId: automationClientId, | |
| 169 | + environmentId: threadRef.environmentId, | |
| 170 | + threadId: threadRef.threadId, | |
| 171 | + tabId: state.snapshot?.tabId ?? null, | |
| 172 | + visible, | |
| 173 | + supportsAutomation: Boolean(previewBridge?.automation), | |
| 174 | + focusedAt: new Date().toISOString(), | |
| 175 | + }, | |
| 176 | + }); | |
| 177 | + }); | |
| 167 | 178 | useEffect(() => { |
| 168 | − ownerStateRef.current = { threadRef, visible }; | |
| 179 | + void reportCurrentAutomationOwner(); | |
| 169 | 180 | }, [threadRef, visible]); |
| 170 | 181 | |
| 171 | 182 | const handleRequest = useCallback( |
| @@ -208,7 +219,7 @@ export function PreviewAutomationOwner(props: { · 36 unmodified lines | ||
| 208 | 219 | return currentStatus(threadRef, input.show ?? true); |
| 209 | 220 | } |
| 210 | 221 | case "navigate": { |
| 211 | − if (!previewBridge || !tabId) throw new Error("Preview tab is not initialized."); | |
| 222 | + if (!previewBridge || !tabId) throw previewTabNotFoundError(); | |
| 212 | 223 | const input = request.input as PreviewAutomationNavigateInput; |
| 213 | 224 | const resolution = resolveBrowserNavigationTarget( |
| 214 | 225 | threadRef.environmentId, |
| @@ -223,46 +234,46 @@ export function PreviewAutomationOwner(props: { · 8 unmodified lines | ||
| 223 | 234 | return currentStatus(threadRef, visible); |
| 224 | 235 | } |
| 225 | 236 | case "snapshot": |
| 226 | − if (!previewBridge || !tabId) throw new Error("Preview tab is not initialized."); | |
| 237 | + if (!previewBridge || !tabId) throw previewTabNotFoundError(); | |
| 227 | 238 | return previewBridge.automation.snapshot(tabId); |
| 228 | 239 | case "click": |
| 229 | − if (!previewBridge || !tabId) throw new Error("Preview tab is not initialized."); | |
| 240 | + if (!previewBridge || !tabId) throw previewTabNotFoundError(); | |
| 230 | 241 | return previewBridge.automation.click( |
| 231 | 242 | tabId, |
| 232 | 243 | request.input as Parameters<typeof previewBridge.automation.click>[1], |
| 233 | 244 | ); |
| 234 | 245 | case "type": |
| 235 | − if (!previewBridge || !tabId) throw new Error("Preview tab is not initialized."); | |
| 246 | + if (!previewBridge || !tabId) throw previewTabNotFoundError(); | |
| 236 | 247 | return previewBridge.automation.type( |
| 237 | 248 | tabId, |
| 238 | 249 | request.input as Parameters<typeof previewBridge.automation.type>[1], |
| 239 | 250 | ); |
| 240 | 251 | case "press": |
| 241 | − if (!previewBridge || !tabId) throw new Error("Preview tab is not initialized."); | |
| 252 | + if (!previewBridge || !tabId) throw previewTabNotFoundError(); | |
| 242 | 253 | return previewBridge.automation.press( |
| 243 | 254 | tabId, |
| 244 | 255 | request.input as Parameters<typeof previewBridge.automation.press>[1], |
| 245 | 256 | ); |
| 246 | 257 | case "scroll": |
| 247 | − if (!previewBridge || !tabId) throw new Error("Preview tab is not initialized."); | |
| 258 | + if (!previewBridge || !tabId) throw previewTabNotFoundError(); | |
| 248 | 259 | return previewBridge.automation.scroll( |
| 249 | 260 | tabId, |
| 250 | 261 | request.input as Parameters<typeof previewBridge.automation.scroll>[1], |
| 251 | 262 | ); |
| 252 | 263 | case "evaluate": |
| 253 | − if (!previewBridge || !tabId) throw new Error("Preview tab is not initialized."); | |
| 264 | + if (!previewBridge || !tabId) throw previewTabNotFoundError(); | |
| 254 | 265 | return previewBridge.automation.evaluate( |
| 255 | 266 | tabId, |
| 256 | 267 | request.input as Parameters<typeof previewBridge.automation.evaluate>[1], |
| 257 | 268 | ); |
| 258 | 269 | case "waitFor": |
| 259 | − if (!previewBridge || !tabId) throw new Error("Preview tab is not initialized."); | |
| 270 | + if (!previewBridge || !tabId) throw previewTabNotFoundError(); | |
| 260 | 271 | return previewBridge.automation.waitFor( |
| 261 | 272 | tabId, |
| 262 | 273 | request.input as Parameters<typeof previewBridge.automation.waitFor>[1], |
| 263 | 274 | ); |
| 264 | 275 | case "recordingStart": { |
| 265 | − if (!tabId) throw new Error("Preview tab is not initialized."); | |
| 276 | + if (!tabId) throw previewTabNotFoundError(); | |
| 266 | 277 | const startedAt = await startBrowserRecording(tabId); |
| 267 | 278 | return { |
| 268 | 279 | tabId, |
| @@ -271,7 +282,7 @@ export function PreviewAutomationOwner(props: { · 2 unmodified lines | ||
| 271 | 282 | }; |
| 272 | 283 | } |
| 273 | 284 | case "recordingStop": { |
| 274 | − if (!tabId) throw new Error("Preview tab is not initialized."); | |
| 285 | + if (!tabId) throw previewTabNotFoundError(); | |
| 275 | 286 | const artifact = await stopBrowserRecording(tabId); |
| 276 | 287 | if (!artifact) throw new Error("No active recording exists for this preview tab."); |
| 277 | 288 | return artifact; |
| @@ -280,34 +291,34 @@ export function PreviewAutomationOwner(props: { · 2 unmodified lines | ||
| 280 | 291 | }, |
| 281 | 292 | [open, threadRef, visible], |
| 282 | 293 | ); |
| 294 | + const [requestHandler] = useState(() => | |
| 295 | + createLatestPreviewAutomationRequestHandler(handleRequest), | |
| 296 | + ); | |
| 283 | 297 | useEffect(() => { |
| 284 | − handlerRef.current = handleRequest; | |
| 285 | − }, [handleRequest]); | |
| 298 | + requestHandler.set(handleRequest); | |
| 299 | + }, [handleRequest, requestHandler]); | |
| 286 | 300 | |
| 287 | − useEffect(() => { | |
| 288 | − const request = automationRequests.data; | |
| 289 | − if (!request) return; | |
| 290 | − void handlerRef.current(request).then( | |
| 291 | − (result) => | |
| 292 | − respondToAutomation({ | |
| 293 | − environmentId: threadRef.environmentId, | |
| 294 | − input: { | |
| 295 | − requestId: request.requestId, | |
| 296 | − ok: true, | |
| 297 | − ...(result === undefined ? {} : { result }), | |
| 298 | − }, | |
| 299 | − }), | |
| 300 | − (error) => | |
| 301 | − respondToAutomation({ | |
| 302 | − environmentId: threadRef.environmentId, | |
| 303 | − input: { | |
| 304 | − requestId: request.requestId, | |
| 305 | − ok: false, | |
| 306 | − error: serializeError(error), | |
| 307 | − }, | |
| 308 | − }), | |
| 309 | − ); | |
| 310 | − }, [automationRequests.data, respondToAutomation, threadRef.environmentId]); | |
| 301 | + const automationRequestConsumerAtom = useMemo( | |
| 302 | + () => | |
| 303 | + createPreviewAutomationRequestConsumerAtom({ | |
| 304 | + requestsAtom: automationRequestsAtom, | |
| 305 | + handleRequest: requestHandler.handle, | |
| 306 | + respond: (response) => | |
| 307 | + respondToAutomation({ | |
| 308 | + environmentId: threadRef.environmentId, | |
| 309 | + input: response, | |
| 310 | + }), | |
| 311 | + label: `preview:automation-request-consumer:${automationClientId}`, | |
| 312 | + }), | |
| 313 | + [ | |
| 314 | + automationClientId, | |
| 315 | + automationRequestsAtom, | |
| 316 | + requestHandler, | |
| 317 | + respondToAutomation, | |
| 318 | + threadRef.environmentId, | |
| 319 | + ], | |
| 320 | + ); | |
| 321 | + useAtomValue(automationRequestConsumerAtom); | |
| 311 | 322 | |
| 312 | 323 | useEffect(() => { |
| 313 | 324 | const observation = observeAutomationOwnerConnectedGeneration( |
| @@ -317,39 +328,11 @@ export function PreviewAutomationOwner(props: { · 3 unmodified lines | ||
| 317 | 328 | connectedGenerationRef.current = observation.nextGeneration; |
| 318 | 329 | if (!observation.shouldReport) return; |
| 319 | 330 | |
| 320 | − const ownerState = ownerStateRef.current; | |
| 321 | − const state = readThreadPreviewState(ownerState.threadRef); | |
| 322 | − void reportAutomationOwner({ | |
| 323 | − environmentId: ownerState.threadRef.environmentId, | |
| 324 | − input: { | |
| 325 | − clientId: automationClientId, | |
| 326 | − environmentId: ownerState.threadRef.environmentId, | |
| 327 | − threadId: ownerState.threadRef.threadId, | |
| 328 | − tabId: state.snapshot?.tabId ?? null, | |
| 329 | − visible: ownerState.visible, | |
| 330 | − supportsAutomation: Boolean(previewBridge?.automation), | |
| 331 | − focusedAt: new Date().toISOString(), | |
| 332 | − }, | |
| 333 | − }); | |
| 334 | − }, [automationClientId, connectedGeneration, reportAutomationOwner]); | |
| 331 | + void reportCurrentAutomationOwner(); | |
| 332 | + }, [connectedGeneration]); | |
| 335 | 333 | |
| 336 | 334 | useEffect(() => { |
| 337 | − const report = () => { | |
| 338 | − const state = readThreadPreviewState(threadRef); | |
| 339 | − void reportAutomationOwner({ | |
| 340 | − environmentId: threadRef.environmentId, | |
| 341 | − input: { | |
| 342 | − clientId: automationClientId, | |
| 343 | − environmentId: threadRef.environmentId, | |
| 344 | − threadId: threadRef.threadId, | |
| 345 | − tabId: state.snapshot?.tabId ?? null, | |
| 346 | − visible, | |
| 347 | − supportsAutomation: Boolean(previewBridge?.automation), | |
| 348 | − focusedAt: new Date().toISOString(), | |
| 349 | − }, | |
| 350 | − }); | |
| 351 | − }; | |
| 352 | − report(); | |
| 335 | + const report = () => void reportCurrentAutomationOwner(); | |
| 353 | 336 | window.addEventListener("focus", report); |
| 354 | 337 | const unsubscribe = subscribeThreadPreviewState(threadRef, (state, previous) => { |
| 355 | 338 | if (state.snapshot?.tabId !== previous.snapshot?.tabId) { |
| @@ -361,10 +344,14 @@ export function PreviewAutomationOwner(props: { · 5 unmodified lines | ||
| 361 | 344 | unsubscribe(); |
| 362 | 345 | void clearAutomationOwner({ |
| 363 | 346 | environmentId: threadRef.environmentId, |
| 364 | − input: { clientId: automationClientId }, | |
| 347 | + input: { | |
| 348 | + clientId: automationClientId, | |
| 349 | + environmentId: threadRef.environmentId, | |
| 350 | + threadId: threadRef.threadId, | |
| 351 | + }, | |
| 365 | 352 | }); |
| 366 | 353 | }; |
| 367 | − }, [automationClientId, clearAutomationOwner, reportAutomationOwner, threadRef, visible]); | |
| 354 | + }, [automationClientId, clearAutomationOwner, threadRef]); | |
| 368 | 355 | |
| 369 | 356 | return null; |
| 370 | 357 | } |
| @@ -0,0 +1,81 @@ | ||
| 1 | +import type { PreviewAutomationRequest, PreviewAutomationResponse } from "@t3tools/contracts"; | |
| 2 | +import { ThreadId } from "@t3tools/contracts"; | |
| 3 | +import { AsyncResult, Atom, AtomRegistry } from "effect/unstable/reactivity"; | |
| 4 | +import { describe, expect, it, vi } from "vite-plus/test"; | |
| 5 | + | |
| 6 | +import { | |
| 7 | + createPreviewAutomationRequestConsumerAtom, | |
| 8 | + serializePreviewAutomationError, | |
| 9 | +} from "./previewAutomationRequestConsumer"; | |
| 10 | + | |
| 11 | +const request = (requestId: string): PreviewAutomationRequest => ({ | |
| 12 | + requestId, | |
| 13 | + threadId: ThreadId.make("thread-1"), | |
| 14 | + operation: "status", | |
| 15 | + input: {}, | |
| 16 | + timeoutMs: 15_000, | |
| 17 | +}); | |
| 18 | + | |
| 19 | +describe("previewAutomationRequestConsumer", () => { | |
| 20 | + it("consumes every request emitted before React can render", async () => { | |
| 21 | + const requestsAtom = Atom.make<AsyncResult.AsyncResult<PreviewAutomationRequest, Error>>( | |
| 22 | + AsyncResult.initial<PreviewAutomationRequest, Error>(false), | |
| 23 | + ); | |
| 24 | + const handleRequest = vi.fn(async (value: PreviewAutomationRequest) => ({ | |
| 25 | + requestId: value.requestId, | |
| 26 | + })); | |
| 27 | + const responses: PreviewAutomationResponse[] = []; | |
| 28 | + const respond = vi.fn(async (response: PreviewAutomationResponse) => { | |
| 29 | + responses.push(response); | |
| 30 | + }); | |
| 31 | + const consumerAtom = createPreviewAutomationRequestConsumerAtom({ | |
| 32 | + requestsAtom, | |
| 33 | + handleRequest, | |
| 34 | + respond, | |
| 35 | + label: "test:preview-automation-consumer", | |
| 36 | + }); | |
| 37 | + const registry = AtomRegistry.make(); | |
| 38 | + registry.mount(consumerAtom); | |
| 39 | + | |
| 40 | + registry.set(requestsAtom, AsyncResult.success(request("request-1"))); | |
| 41 | + registry.set(requestsAtom, AsyncResult.success(request("request-2"))); | |
| 42 | + | |
| 43 | + await vi.waitFor(() => expect(respond).toHaveBeenCalledTimes(2)); | |
| 44 | + expect(handleRequest.mock.calls.map(([value]) => value.requestId)).toEqual([ | |
| 45 | + "request-1", | |
| 46 | + "request-2", | |
| 47 | + ]); | |
| 48 | + expect(responses.map((response) => response.requestId)).toEqual(["request-1", "request-2"]); | |
| 49 | + registry.dispose(); | |
| 50 | + }); | |
| 51 | + | |
| 52 | + it("consumes a request that arrived immediately before the consumer mounted", async () => { | |
| 53 | + const requestsAtom = Atom.make( | |
| 54 | + AsyncResult.success<PreviewAutomationRequest, Error>(request("request-ready")), | |
| 55 | + ); | |
| 56 | + const respond = vi.fn(async (_response: PreviewAutomationResponse) => undefined); | |
| 57 | + const consumerAtom = createPreviewAutomationRequestConsumerAtom({ | |
| 58 | + requestsAtom, | |
| 59 | + handleRequest: async () => undefined, | |
| 60 | + respond, | |
| 61 | + label: "test:preview-automation-initial-request", | |
| 62 | + }); | |
| 63 | + const registry = AtomRegistry.make(); | |
| 64 | + | |
| 65 | + registry.mount(consumerAtom); | |
| 66 | + | |
| 67 | + await vi.waitFor(() => expect(respond).toHaveBeenCalledTimes(1)); | |
| 68 | + expect(respond).toHaveBeenCalledWith({ requestId: "request-ready", ok: true }); | |
| 69 | + registry.dispose(); | |
| 70 | + }); | |
| 71 | + | |
| 72 | + it("preserves typed automation errors in responses", () => { | |
| 73 | + const error = new Error("No preview tab"); | |
| 74 | + error.name = "PreviewAutomationTabNotFoundError"; | |
| 75 | + | |
| 76 | + expect(serializePreviewAutomationError(error)).toEqual({ | |
| 77 | + _tag: "PreviewAutomationTabNotFoundError", | |
| 78 | + message: "No preview tab", | |
| 79 | + }); | |
| 80 | + }); | |
| 81 | +}); | |
| @@ -0,0 +1,83 @@ | ||
| 1 | +import type { PreviewAutomationRequest, PreviewAutomationResponse } from "@t3tools/contracts"; | |
| 2 | +import { AsyncResult, Atom } from "effect/unstable/reactivity"; | |
| 3 | + | |
| 4 | +type AutomationRequestResult<E> = AsyncResult.AsyncResult<PreviewAutomationRequest, E>; | |
| 5 | +type AutomationRequestHandler = (request: PreviewAutomationRequest) => Promise<unknown>; | |
| 6 | + | |
| 7 | +export function createLatestPreviewAutomationRequestHandler(initial: AutomationRequestHandler): { | |
| 8 | + readonly set: (handler: AutomationRequestHandler) => void; | |
| 9 | + readonly handle: AutomationRequestHandler; | |
| 10 | +} { | |
| 11 | + let current = initial; | |
| 12 | + return { | |
| 13 | + set: (handler) => { | |
| 14 | + current = handler; | |
| 15 | + }, | |
| 16 | + handle: (request) => current(request), | |
| 17 | + }; | |
| 18 | +} | |
| 19 | + | |
| 20 | +export function serializePreviewAutomationError( | |
| 21 | + error: unknown, | |
| 22 | +): NonNullable<PreviewAutomationResponse["error"]> { | |
| 23 | + if (error instanceof Error) { | |
| 24 | + const detail = | |
| 25 | + "detail" in error && (error as { detail?: unknown }).detail !== undefined | |
| 26 | + ? (error as { detail?: unknown }).detail | |
| 27 | + : undefined; | |
| 28 | + return { | |
| 29 | + _tag: error.name.startsWith("PreviewAutomation") | |
| 30 | + ? error.name | |
| 31 | + : "PreviewAutomationExecutionError", | |
| 32 | + message: error.message, | |
| 33 | + ...(detail === undefined ? {} : { detail }), | |
| 34 | + }; | |
| 35 | + } | |
| 36 | + return { | |
| 37 | + _tag: "PreviewAutomationExecutionError", | |
| 38 | + message: String(error), | |
| 39 | + }; | |
| 40 | +} | |
| 41 | + | |
| 42 | +export function createPreviewAutomationRequestConsumerAtom<E>(options: { | |
| 43 | + readonly requestsAtom: Atom.Atom<AutomationRequestResult<E>>; | |
| 44 | + readonly handleRequest: (request: PreviewAutomationRequest) => Promise<unknown>; | |
| 45 | + readonly respond: (response: PreviewAutomationResponse) => Promise<unknown>; | |
| 46 | + readonly label: string; | |
| 47 | +}): Atom.Atom<void> { | |
| 48 | + return Atom.make((get) => { | |
| 49 | + let disposed = false; | |
| 50 | + let requestsVersion = 0; | |
| 51 | + | |
| 52 | + const consume = (result: AutomationRequestResult<E>) => { | |
| 53 | + if (!AsyncResult.isSuccess(result)) return; | |
| 54 | + const request = result.value; | |
| 55 | + void options.handleRequest(request).then( | |
| 56 | + (value) => | |
| 57 | + options.respond({ | |
| 58 | + requestId: request.requestId, | |
| 59 | + ok: true, | |
| 60 | + ...(value === undefined ? {} : { result: value }), | |
| 61 | + }), | |
| 62 | + (error) => | |
| 63 | + options.respond({ | |
| 64 | + requestId: request.requestId, | |
| 65 | + ok: false, | |
| 66 | + error: serializePreviewAutomationError(error), | |
| 67 | + }), | |
| 68 | + ); | |
| 69 | + }; | |
| 70 | + | |
| 71 | + get.addFinalizer(() => { | |
| 72 | + disposed = true; | |
| 73 | + }); | |
| 74 | + const initialRequest = get.once(options.requestsAtom); | |
| 75 | + get.subscribe(options.requestsAtom, (result) => { | |
| 76 | + requestsVersion += 1; | |
| 77 | + consume(result); | |
| 78 | + }); | |
| 79 | + queueMicrotask(() => { | |
| 80 | + if (!disposed && requestsVersion === 0) consume(initialRequest); | |
| 81 | + }); | |
| 82 | + }).pipe(Atom.setIdleTTL(0), Atom.withLabel(options.label)); | |
| 83 | +} | |
| @@ -0,0 +1,68 @@ | ||
| 1 | +import { scopeThreadRef } from "@t3tools/client-runtime/environment"; | |
| 2 | +import { EnvironmentId, ThreadId, TurnId } from "@t3tools/contracts"; | |
| 3 | +import { beforeEach, describe, expect, it } from "vite-plus/test"; | |
| 4 | + | |
| 5 | +import { selectThreadDiffPanelSelection, useDiffPanelStore } from "./diffPanelStore"; | |
| 6 | + | |
| 7 | +const THREAD_REF = scopeThreadRef(EnvironmentId.make("environment-1"), ThreadId.make("thread-1")); | |
| 8 | + | |
| 9 | +describe("diffPanelStore", () => { | |
| 10 | + beforeEach(() => useDiffPanelStore.setState({ byThreadKey: {}, branchBaseRefByThreadKey: {} })); | |
| 11 | + | |
| 12 | + it("defaults each thread to branch changes with automatic base selection", () => { | |
| 13 | + expect( | |
| 14 | + selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF), | |
| 15 | + ).toEqual({ kind: "branch", baseRef: null }); | |
| 16 | + }); | |
| 17 | + | |
| 18 | + it("clears incompatible selection fields when changing scopes", () => { | |
| 19 | + const store = useDiffPanelStore.getState(); | |
| 20 | + store.selectTurn(THREAD_REF, TurnId.make("turn-1"), "src/app.ts"); | |
| 21 | + store.selectGitScope(THREAD_REF, "unstaged"); | |
| 22 | + | |
| 23 | + expect( | |
| 24 | + selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF), | |
| 25 | + ).toEqual({ kind: "unstaged" }); | |
| 26 | + | |
| 27 | + useDiffPanelStore.getState().selectBranchBaseRef(THREAD_REF, " origin/main "); | |
| 28 | + expect( | |
| 29 | + selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF), | |
| 30 | + ).toEqual({ kind: "branch", baseRef: "origin/main" }); | |
| 31 | + }); | |
| 32 | + | |
| 33 | + it("increments the reveal request when opening the same turn file again", () => { | |
| 34 | + const turnId = TurnId.make("turn-1"); | |
| 35 | + useDiffPanelStore.getState().selectTurn(THREAD_REF, turnId, "src/app.ts"); | |
| 36 | + useDiffPanelStore.getState().selectTurn(THREAD_REF, turnId, "src/app.ts"); | |
| 37 | + | |
| 38 | + expect( | |
| 39 | + selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF), | |
| 40 | + ).toEqual({ kind: "turn", turnId, filePath: "src/app.ts", revealRequestId: 2 }); | |
| 41 | + }); | |
| 42 | + | |
| 43 | + it("restores the selected branch base after visiting another scope", () => { | |
| 44 | + useDiffPanelStore.getState().selectBranchBaseRef(THREAD_REF, "origin/main"); | |
| 45 | + useDiffPanelStore.getState().selectGitScope(THREAD_REF, "unstaged"); | |
| 46 | + useDiffPanelStore.getState().selectGitScope(THREAD_REF, "branch"); | |
| 47 | + | |
| 48 | + expect( | |
| 49 | + selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF), | |
| 50 | + ).toEqual({ kind: "branch", baseRef: "origin/main" }); | |
| 51 | + }); | |
| 52 | + | |
| 53 | + it("reconciles a missing turn selection to the latest available turn", () => { | |
| 54 | + const missingTurnId = TurnId.make("turn-missing"); | |
| 55 | + const latestTurnId = TurnId.make("turn-latest"); | |
| 56 | + useDiffPanelStore.getState().selectTurn(THREAD_REF, missingTurnId, "src/app.ts"); | |
| 57 | + useDiffPanelStore.getState().reconcileTurnSelection(THREAD_REF, [latestTurnId]); | |
| 58 | + | |
| 59 | + expect( | |
| 60 | + selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF), | |
| 61 | + ).toEqual({ | |
| 62 | + kind: "turn", | |
| 63 | + turnId: latestTurnId, | |
| 64 | + filePath: "src/app.ts", | |
| 65 | + revealRequestId: 1, | |
| 66 | + }); | |
| 67 | + }); | |
| 68 | +}); | |
| @@ -0,0 +1,139 @@ | ||
| 1 | +import { scopedThreadKey } from "@t3tools/client-runtime/environment"; | |
| 2 | +import type { ScopedThreadRef, TurnId } from "@t3tools/contracts"; | |
| 3 | +import { create } from "zustand"; | |
| 4 | +import { createJSONStorage, persist } from "zustand/middleware"; | |
| 5 | + | |
| 6 | +import { resolveStorage } from "./lib/storage"; | |
| 7 | + | |
| 8 | +export type DiffPanelSelection = | |
| 9 | + | { kind: "branch"; baseRef: string | null } | |
| 10 | + | { kind: "unstaged" } | |
| 11 | + | { kind: "turn"; turnId: TurnId; filePath: string | null; revealRequestId: number }; | |
| 12 | + | |
| 13 | +const DEFAULT_SELECTION: DiffPanelSelection = { kind: "branch", baseRef: null }; | |
| 14 | + | |
| 15 | +interface DiffPanelStoreState { | |
| 16 | + byThreadKey: Record<string, DiffPanelSelection>; | |
| 17 | + branchBaseRefByThreadKey: Record<string, string | null>; | |
| 18 | + selectGitScope: (ref: ScopedThreadRef, scope: "branch" | "unstaged") => void; | |
| 19 | + selectBranchBaseRef: (ref: ScopedThreadRef, baseRef: string | null) => void; | |
| 20 | + selectTurn: (ref: ScopedThreadRef, turnId: TurnId, filePath?: string) => void; | |
| 21 | + reconcileTurnSelection: (ref: ScopedThreadRef, availableTurnIds: ReadonlyArray<TurnId>) => void; | |
| 22 | + removeThread: (ref: ScopedThreadRef) => void; | |
| 23 | +} | |
| 24 | + | |
| 25 | +function normalizeBaseRef(baseRef: string | null): string | null { | |
| 26 | + const normalized = baseRef?.trim(); | |
| 27 | + return normalized ? normalized : null; | |
| 28 | +} | |
| 29 | + | |
| 30 | +export const useDiffPanelStore = create<DiffPanelStoreState>()( | |
| 31 | + persist( | |
| 32 | + (set) => ({ | |
| 33 | + byThreadKey: {}, | |
| 34 | + branchBaseRefByThreadKey: {}, | |
| 35 | + selectGitScope: (ref, scope) => | |
| 36 | + set((state) => { | |
| 37 | + const threadKey = scopedThreadKey(ref); | |
| 38 | + const previous = state.byThreadKey[threadKey]; | |
| 39 | + const previousBaseRef = | |
| 40 | + previous?.kind === "branch" | |
| 41 | + ? previous.baseRef | |
| 42 | + : (state.branchBaseRefByThreadKey[threadKey] ?? null); | |
| 43 | + return { | |
| 44 | + byThreadKey: { | |
| 45 | + ...state.byThreadKey, | |
| 46 | + [threadKey]: | |
| 47 | + scope === "branch" | |
| 48 | + ? { kind: "branch", baseRef: previousBaseRef } | |
| 49 | + : { kind: "unstaged" }, | |
| 50 | + }, | |
| 51 | + branchBaseRefByThreadKey: | |
| 52 | + previous?.kind === "branch" | |
| 53 | + ? { ...state.branchBaseRefByThreadKey, [threadKey]: previous.baseRef } | |
| 54 | + : state.branchBaseRefByThreadKey, | |
| 55 | + }; | |
| 56 | + }), | |
| 57 | + selectBranchBaseRef: (ref, baseRef) => | |
| 58 | + set((state) => { | |
| 59 | + const threadKey = scopedThreadKey(ref); | |
| 60 | + const normalizedBaseRef = normalizeBaseRef(baseRef); | |
| 61 | + return { | |
| 62 | + byThreadKey: { | |
| 63 | + ...state.byThreadKey, | |
| 64 | + [threadKey]: { kind: "branch", baseRef: normalizedBaseRef }, | |
| 65 | + }, | |
| 66 | + branchBaseRefByThreadKey: { | |
| 67 | + ...state.branchBaseRefByThreadKey, | |
| 68 | + [threadKey]: normalizedBaseRef, | |
| 69 | + }, | |
| 70 | + }; | |
| 71 | + }), | |
| 72 | + selectTurn: (ref, turnId, filePath) => | |
| 73 | + set((state) => { | |
| 74 | + const threadKey = scopedThreadKey(ref); | |
| 75 | + const previous = state.byThreadKey[threadKey]; | |
| 76 | + return { | |
| 77 | + byThreadKey: { | |
| 78 | + ...state.byThreadKey, | |
| 79 | + [threadKey]: { | |
| 80 | + kind: "turn", | |
| 81 | + turnId, | |
| 82 | + filePath: filePath?.trim() || null, | |
| 83 | + revealRequestId: previous?.kind === "turn" ? previous.revealRequestId + 1 : 1, | |
| 84 | + }, | |
| 85 | + }, | |
| 86 | + }; | |
| 87 | + }), | |
| 88 | + reconcileTurnSelection: (ref, availableTurnIds) => | |
| 89 | + set((state) => { | |
| 90 | + const threadKey = scopedThreadKey(ref); | |
| 91 | + const previous = state.byThreadKey[threadKey]; | |
| 92 | + const latestTurnId = availableTurnIds[0]; | |
| 93 | + if ( | |
| 94 | + previous?.kind !== "turn" || | |
| 95 | + latestTurnId === undefined || | |
| 96 | + availableTurnIds.includes(previous.turnId) | |
| 97 | + ) { | |
| 98 | + return state; | |
| 99 | + } | |
| 100 | + return { | |
| 101 | + byThreadKey: { | |
| 102 | + ...state.byThreadKey, | |
| 103 | + [threadKey]: { ...previous, turnId: latestTurnId }, | |
| 104 | + }, | |
| 105 | + }; | |
| 106 | + }), | |
| 107 | + removeThread: (ref) => | |
| 108 | + set((state) => { | |
| 109 | + const threadKey = scopedThreadKey(ref); | |
| 110 | + if (!(threadKey in state.byThreadKey) && !(threadKey in state.branchBaseRefByThreadKey)) { | |
| 111 | + return state; | |
| 112 | + } | |
| 113 | + const { [threadKey]: _removed, ...byThreadKey } = state.byThreadKey; | |
| 114 | + const { [threadKey]: _removedBaseRef, ...branchBaseRefByThreadKey } = | |
| 115 | + state.branchBaseRefByThreadKey; | |
| 116 | + return { byThreadKey, branchBaseRefByThreadKey }; | |
| 117 | + }), | |
| 118 | + }), | |
| 119 | + { | |
| 120 | + name: "t3code:diff-panel-state:v1", | |
| 121 | + version: 1, | |
| 122 | + storage: createJSONStorage(() => | |
| 123 | + resolveStorage(typeof window !== "undefined" ? window.localStorage : undefined), | |
| 124 | + ), | |
| 125 | + partialize: (state) => ({ | |
| 126 | + byThreadKey: state.byThreadKey, | |
| 127 | + branchBaseRefByThreadKey: state.branchBaseRefByThreadKey, | |
| 128 | + }), | |
| 129 | + }, | |
| 130 | + ), | |
| 131 | +); | |
| 132 | + | |
| 133 | +export function selectThreadDiffPanelSelection( | |
| 134 | + byThreadKey: Record<string, DiffPanelSelection>, | |
| 135 | + ref: ScopedThreadRef | null | undefined, | |
| 136 | +): DiffPanelSelection { | |
| 137 | + if (!ref) return DEFAULT_SELECTION; | |
| 138 | + return byThreadKey[scopedThreadKey(ref)] ?? DEFAULT_SELECTION; | |
| 139 | +} | |
| @@ -1,74 +0,0 @@ | ||
| 1 | −import { describe, expect, it } from "vite-plus/test"; | |
| 2 | − | |
| 3 | −import { parseDiffRouteSearch } from "./diffRouteSearch"; | |
| 4 | − | |
| 5 | −describe("parseDiffRouteSearch", () => { | |
| 6 | − it("parses valid diff search values", () => { | |
| 7 | − const parsed = parseDiffRouteSearch({ | |
| 8 | − diff: "1", | |
| 9 | − diffTurnId: "turn-1", | |
| 10 | − diffFilePath: "src/app.ts", | |
| 11 | − }); | |
| 12 | − | |
| 13 | − expect(parsed).toEqual({ | |
| 14 | − diff: "1", | |
| 15 | − diffTurnId: "turn-1", | |
| 16 | − diffFilePath: "src/app.ts", | |
| 17 | − }); | |
| 18 | − }); | |
| 19 | − | |
| 20 | − it("treats numeric and boolean diff toggles as open", () => { | |
| 21 | − expect( | |
| 22 | − parseDiffRouteSearch({ | |
| 23 | − diff: 1, | |
| 24 | − diffTurnId: "turn-1", | |
| 25 | − }), | |
| 26 | − ).toEqual({ | |
| 27 | − diff: "1", | |
| 28 | − diffTurnId: "turn-1", | |
| 29 | − }); | |
| 30 | − | |
| 31 | − expect( | |
| 32 | − parseDiffRouteSearch({ | |
| 33 | − diff: true, | |
| 34 | − diffTurnId: "turn-1", | |
| 35 | − }), | |
| 36 | − ).toEqual({ | |
| 37 | − diff: "1", | |
| 38 | − diffTurnId: "turn-1", | |
| 39 | − }); | |
| 40 | − }); | |
| 41 | − | |
| 42 | − it("drops turn and file values when diff is closed", () => { | |
| 43 | − const parsed = parseDiffRouteSearch({ | |
| 44 | − diff: "0", | |
| 45 | − diffTurnId: "turn-1", | |
| 46 | − diffFilePath: "src/app.ts", | |
| 47 | − }); | |
| 48 | − | |
| 49 | − expect(parsed).toEqual({}); | |
| 50 | − }); | |
| 51 | − | |
| 52 | − it("drops file value when turn is not selected", () => { | |
| 53 | − const parsed = parseDiffRouteSearch({ | |
| 54 | − diff: "1", | |
| 55 | − diffFilePath: "src/app.ts", | |
| 56 | − }); | |
| 57 | − | |
| 58 | − expect(parsed).toEqual({ | |
| 59 | − diff: "1", | |
| 60 | − }); | |
| 61 | − }); | |
| 62 | − | |
| 63 | − it("normalizes whitespace-only values", () => { | |
| 64 | − const parsed = parseDiffRouteSearch({ | |
| 65 | − diff: "1", | |
| 66 | − diffTurnId: " ", | |
| 67 | − diffFilePath: " ", | |
| 68 | − }); | |
| 69 | − | |
| 70 | − expect(parsed).toEqual({ | |
| 71 | − diff: "1", | |
| 72 | − }); | |
| 73 | − }); | |
| 74 | −}); | |
| @@ -1,39 +0,0 @@ | ||
| 1 | −import { TurnId } from "@t3tools/contracts"; | |
| 2 | − | |
| 3 | −export interface DiffRouteSearch { | |
| 4 | − diff?: "1" | undefined; | |
| 5 | − diffTurnId?: TurnId | undefined; | |
| 6 | − diffFilePath?: string | undefined; | |
| 7 | −} | |
| 8 | − | |
| 9 | −function isDiffOpenValue(value: unknown): boolean { | |
| 10 | − return value === "1" || value === 1 || value === true; | |
| 11 | −} | |
| 12 | − | |
| 13 | −function normalizeSearchString(value: unknown): string | undefined { | |
| 14 | − if (typeof value !== "string") { | |
| 15 | − return undefined; | |
| 16 | − } | |
| 17 | − const normalized = value.trim(); | |
| 18 | − return normalized.length > 0 ? normalized : undefined; | |
| 19 | −} | |
| 20 | − | |
| 21 | −export function stripDiffSearchParams<T extends Record<string, unknown>>( | |
| 22 | − params: T, | |
| 23 | −): Omit<T, "diff" | "diffTurnId" | "diffFilePath"> { | |
| 24 | − const { diff: _diff, diffTurnId: _diffTurnId, diffFilePath: _diffFilePath, ...rest } = params; | |
| 25 | − return rest as Omit<T, "diff" | "diffTurnId" | "diffFilePath">; | |
| 26 | −} | |
| 27 | − | |
| 28 | −export function parseDiffRouteSearch(search: Record<string, unknown>): DiffRouteSearch { | |
| 29 | − const diff = isDiffOpenValue(search.diff) ? "1" : undefined; | |
| 30 | − const diffTurnIdRaw = diff ? normalizeSearchString(search.diffTurnId) : undefined; | |
| 31 | − const diffTurnId = diffTurnIdRaw ? TurnId.make(diffTurnIdRaw) : undefined; | |
| 32 | − const diffFilePath = diff && diffTurnId ? normalizeSearchString(search.diffFilePath) : undefined; | |
| 33 | − | |
| 34 | − return { | |
| 35 | − ...(diff ? { diff } : {}), | |
| 36 | − ...(diffTurnId ? { diffTurnId } : {}), | |
| 37 | − ...(diffFilePath ? { diffFilePath } : {}), | |
| 38 | − }; | |
| 39 | −} | |
| @@ -5,16 +5,13 @@ import * as Layer from "effect/Layer"; · 4 unmodified lines | ||
| 5 | 5 | |
| 6 | 6 | import { resolvePrimaryEnvironmentHttpUrl } from "./target"; |
| 7 | 7 | |
| 8 | −export type PrimaryEnvironmentHttpClientShape = Effect.Success< | |
| 9 | − ReturnType<typeof makeEnvironmentHttpApiClient> | |
| 10 | −>; | |
| 11 | − | |
| 12 | 8 | export class PrimaryEnvironmentHttpClient extends Context.Service< |
| 13 | 9 | PrimaryEnvironmentHttpClient, |
| 14 | − PrimaryEnvironmentHttpClientShape | |
| 10 | + Effect.Success<ReturnType<typeof makeEnvironmentHttpApiClient>> | |
| 15 | 11 | >()("@t3tools/web/environments/primary/httpClient/PrimaryEnvironmentHttpClient") {} |
| 16 | 12 | |
| 17 | −export const primaryEnvironmentHttpClientLive = Layer.effect( | |
| 18 | − PrimaryEnvironmentHttpClient, | |
| 19 | − Effect.suspend(() => makeEnvironmentHttpApiClient(resolvePrimaryEnvironmentHttpUrl("/"))), | |
| 13 | +const make = Effect.suspend(() => | |
| 14 | + makeEnvironmentHttpApiClient(resolvePrimaryEnvironmentHttpUrl("/")), | |
| 20 | 15 | ); |
| 16 | + | |
| 17 | +export const layer = Layer.effect(PrimaryEnvironmentHttpClient, make); | |
| @@ -689,7 +689,8 @@ label:has(> select#reasoning-effort) select { · 688 unmodified lines | ||
| 689 | 689 | background: color-mix(in srgb, var(--background) 94%, var(--card)); |
| 690 | 690 | } |
| 691 | 691 | |
| 692 | −.diff-render-file { | |
| 692 | +.diff-render-file, | |
| 693 | +.diff-render-surface > diffs-container { | |
| 693 | 694 | border: 1px solid var(--border); |
| 694 | 695 | border-radius: 0.5rem; |
| 695 | 696 | overflow: clip; |
| @@ -0,0 +1,52 @@ | ||
| 1 | +import { describe, expect, it } from "vite-plus/test"; | |
| 2 | +import type { VcsRef } from "@t3tools/contracts"; | |
| 3 | +import { buildBaseRefChoices, filterBaseRefChoices } from "./baseRefChoices"; | |
| 4 | + | |
| 5 | +function ref(name: string, remoteName?: string): VcsRef { | |
| 6 | + return { | |
| 7 | + name, | |
| 8 | + current: false, | |
| 9 | + isDefault: false, | |
| 10 | + isRemote: remoteName !== undefined, | |
| 11 | + ...(remoteName ? { remoteName } : {}), | |
| 12 | + worktreePath: null, | |
| 13 | + }; | |
| 14 | +} | |
| 15 | + | |
| 16 | +describe("buildBaseRefChoices", () => { | |
| 17 | + it("pairs matching local and remote branches and prefers origin", () => { | |
| 18 | + const choices = buildBaseRefChoices( | |
| 19 | + [ref("main")], | |
| 20 | + [ref("upstream/main", "upstream"), ref("origin/main", "origin")], | |
| 21 | + ); | |
| 22 | + | |
| 23 | + expect(choices).toEqual([ | |
| 24 | + expect.objectContaining({ | |
| 25 | + label: "main", | |
| 26 | + local: expect.objectContaining({ name: "main" }), | |
| 27 | + remote: expect.objectContaining({ name: "origin/main" }), | |
| 28 | + }), | |
| 29 | + expect.objectContaining({ | |
| 30 | + label: "upstream/main", | |
| 31 | + local: null, | |
| 32 | + remote: expect.objectContaining({ name: "upstream/main" }), | |
| 33 | + }), | |
| 34 | + ]); | |
| 35 | + }); | |
| 36 | +}); | |
| 37 | + | |
| 38 | +describe("filterBaseRefChoices", () => { | |
| 39 | + it("filters stale server results against the current query", () => { | |
| 40 | + const choices = buildBaseRefChoices( | |
| 41 | + [ref("main"), ref("feature/search")], | |
| 42 | + [ref("origin/main", "origin"), ref("origin/feature/search", "origin")], | |
| 43 | + ); | |
| 44 | + | |
| 45 | + expect(filterBaseRefChoices(choices, "SEARCH").map((choice) => choice.label)).toEqual([ | |
| 46 | + "feature/search", | |
| 47 | + ]); | |
| 48 | + expect(filterBaseRefChoices(choices, "origin/main").map((choice) => choice.label)).toEqual([ | |
| 49 | + "main", | |
| 50 | + ]); | |
| 51 | + }); | |
| 52 | +}); | |
| @@ -0,0 +1,61 @@ | ||
| 1 | +import type { VcsRef } from "@t3tools/contracts"; | |
| 2 | + | |
| 3 | +export interface BaseRefChoice { | |
| 4 | + readonly id: string; | |
| 5 | + readonly label: string; | |
| 6 | + readonly local: VcsRef | null; | |
| 7 | + readonly remote: VcsRef | null; | |
| 8 | +} | |
| 9 | + | |
| 10 | +function remoteBranchName(ref: VcsRef): string { | |
| 11 | + if (ref.remoteName && ref.name.startsWith(`${ref.remoteName}/`)) { | |
| 12 | + return ref.name.slice(ref.remoteName.length + 1); | |
| 13 | + } | |
| 14 | + return ref.name; | |
| 15 | +} | |
| 16 | + | |
| 17 | +export function buildBaseRefChoices( | |
| 18 | + localRefs: ReadonlyArray<VcsRef>, | |
| 19 | + remoteRefs: ReadonlyArray<VcsRef>, | |
| 20 | +): ReadonlyArray<BaseRefChoice> { | |
| 21 | + const unusedRemoteRefs = new Set(remoteRefs); | |
| 22 | + const pairedChoices = localRefs.map((local) => { | |
| 23 | + const matches = remoteRefs.filter( | |
| 24 | + (remote) => unusedRemoteRefs.has(remote) && remoteBranchName(remote) === local.name, | |
| 25 | + ); | |
| 26 | + const remote = | |
| 27 | + matches.find((candidate) => candidate.remoteName === "origin") ?? matches[0] ?? null; | |
| 28 | + if (remote) unusedRemoteRefs.delete(remote); | |
| 29 | + return { | |
| 30 | + id: `local:${local.name}`, | |
| 31 | + label: local.name, | |
| 32 | + local, | |
| 33 | + remote, | |
| 34 | + }; | |
| 35 | + }); | |
| 36 | + | |
| 37 | + const remoteOnlyChoices = remoteRefs | |
| 38 | + .filter((remote) => unusedRemoteRefs.has(remote)) | |
| 39 | + .map((remote) => ({ | |
| 40 | + id: `remote:${remote.name}`, | |
| 41 | + label: remote.name, | |
| 42 | + local: null, | |
| 43 | + remote, | |
| 44 | + })); | |
| 45 | + | |
| 46 | + return [...pairedChoices, ...remoteOnlyChoices]; | |
| 47 | +} | |
| 48 | + | |
| 49 | +export function filterBaseRefChoices( | |
| 50 | + choices: ReadonlyArray<BaseRefChoice>, | |
| 51 | + query: string, | |
| 52 | +): ReadonlyArray<BaseRefChoice> { | |
| 53 | + const normalizedQuery = query.trim().toLocaleLowerCase(); | |
| 54 | + if (normalizedQuery.length === 0) return choices; | |
| 55 | + return choices.filter( | |
| 56 | + (choice) => | |
| 57 | + choice.label.toLocaleLowerCase().includes(normalizedQuery) || | |
| 58 | + choice.local?.name.toLocaleLowerCase().includes(normalizedQuery) === true || | |
| 59 | + choice.remote?.name.toLocaleLowerCase().includes(normalizedQuery) === true, | |
| 60 | + ); | |
| 61 | +} | |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | −import { buildPatchCacheKey } from "./diffRendering"; | |
| 2 | +import { buildPatchCacheKey, getRenderablePatch } from "./diffRendering"; | |
| 3 | 3 | |
| 4 | 4 | describe("buildPatchCacheKey", () => { |
| 5 | 5 | it("returns a stable cache key for identical content", () => { |
| @@ -29,3 +29,56 @@ describe("buildPatchCacheKey", () => { · 23 unmodified lines | ||
| 29 | 29 | ); |
| 30 | 30 | }); |
| 31 | 31 | }); |
| 32 | + | |
| 33 | +describe("getRenderablePatch", () => { | |
| 34 | + it("compacts partial hunk render offsets for virtualized review diffs", () => { | |
| 35 | + const patch = [ | |
| 36 | + "diff --git a/example.ts b/example.ts", | |
| 37 | + "index 1111111..2222222 100644", | |
| 38 | + "--- a/example.ts", | |
| 39 | + "+++ b/example.ts", | |
| 40 | + "@@ -48,4 +48,4 @@", | |
| 41 | + " context", | |
| 42 | + "-before", | |
| 43 | + "+after", | |
| 44 | + " context", | |
| 45 | + " context", | |
| 46 | + "@@ -80,3 +80,4 @@", | |
| 47 | + " context", | |
| 48 | + "+added", | |
| 49 | + " context", | |
| 50 | + " context", | |
| 51 | + ].join("\n"); | |
| 52 | + | |
| 53 | + const parsed = getRenderablePatch(patch, "review", { | |
| 54 | + compactPartialHunkOffsets: true, | |
| 55 | + }); | |
| 56 | + expect(parsed?.kind).toBe("files"); | |
| 57 | + if (parsed?.kind !== "files") return; | |
| 58 | + | |
| 59 | + const file = parsed.files[0]; | |
| 60 | + expect(file?.hunks[0]?.collapsedBefore).toBe(47); | |
| 61 | + expect(file?.hunks[0]?.unifiedLineStart).toBe(0); | |
| 62 | + expect(file?.hunks[1]?.collapsedBefore).toBeGreaterThan(0); | |
| 63 | + expect(file?.hunks[1]?.unifiedLineStart).toBe(file?.hunks[0]?.unifiedLineCount); | |
| 64 | + expect(file?.unifiedLineCount).toBe( | |
| 65 | + file?.hunks.reduce((total, hunk) => total + hunk.unifiedLineCount, 0), | |
| 66 | + ); | |
| 67 | + }); | |
| 68 | + | |
| 69 | + it("retains source-file offsets for checkpoint diffs", () => { | |
| 70 | + const patch = [ | |
| 71 | + "diff --git a/example.ts b/example.ts", | |
| 72 | + "--- a/example.ts", | |
| 73 | + "+++ b/example.ts", | |
| 74 | + "@@ -48,1 +48,1 @@", | |
| 75 | + "-before", | |
| 76 | + "+after", | |
| 77 | + ].join("\n"); | |
| 78 | + | |
| 79 | + const parsed = getRenderablePatch(patch, "checkpoint"); | |
| 80 | + expect(parsed?.kind).toBe("files"); | |
| 81 | + if (parsed?.kind !== "files") return; | |
| 82 | + expect(parsed.files[0]?.hunks[0]?.unifiedLineStart).toBe(47); | |
| 83 | + }); | |
| 84 | +}); | |
| @@ -52,9 +52,45 @@ export type RenderablePatch = · 51 unmodified lines | ||
| 52 | 52 | reason: string; |
| 53 | 53 | }; |
| 54 | 54 | |
| 55 | +interface RenderablePatchOptions { | |
| 56 | + /** | |
| 57 | + * Pierre's partial-patch parser keeps hunk render starts in source-file | |
| 58 | + * coordinates. Its virtualizer iterates partial patches as compact rows, so | |
| 59 | + * review diffs need compact render starts while retaining collapsedBefore | |
| 60 | + * for the "N unmodified lines" separator. | |
| 61 | + */ | |
| 62 | + compactPartialHunkOffsets?: boolean; | |
| 63 | +} | |
| 64 | + | |
| 65 | +export function compactPartialHunkOffsets(file: FileDiffMetadata): FileDiffMetadata { | |
| 66 | + if (!file.isPartial) return file; | |
| 67 | + | |
| 68 | + let splitLineStart = 0; | |
| 69 | + let unifiedLineStart = 0; | |
| 70 | + const hunks = file.hunks.map((hunk) => { | |
| 71 | + const compactHunk = { | |
| 72 | + ...hunk, | |
| 73 | + splitLineStart, | |
| 74 | + unifiedLineStart, | |
| 75 | + }; | |
| 76 | + splitLineStart += hunk.splitLineCount; | |
| 77 | + unifiedLineStart += hunk.unifiedLineCount; | |
| 78 | + return compactHunk; | |
| 79 | + }); | |
| 80 | + | |
| 81 | + return { | |
| 82 | + ...file, | |
| 83 | + hunks, | |
| 84 | + splitLineCount: splitLineStart, | |
| 85 | + unifiedLineCount: unifiedLineStart, | |
| 86 | + ...(file.cacheKey ? { cacheKey: `${file.cacheKey}:compact-partial` } : {}), | |
| 87 | + }; | |
| 88 | +} | |
| 89 | + | |
| 55 | 90 | export function getRenderablePatch( |
| 56 | 91 | patch: string | undefined, |
| 57 | 92 | cacheScope = "diff-panel", |
| 93 | + options: RenderablePatchOptions = {}, | |
| 58 | 94 | ): RenderablePatch | null { |
| 59 | 95 | if (!patch) return null; |
| 60 | 96 | const normalizedPatch = patch.trim(); |
| @@ -65,7 +101,11 @@ export function getRenderablePatch( · 4 unmodified lines | ||
| 65 | 101 | normalizedPatch, |
| 66 | 102 | buildPatchCacheKey(normalizedPatch, cacheScope), |
| 67 | 103 | ); |
| 68 | − const files = parsedPatches.flatMap((parsedPatch) => parsedPatch.files); | |
| 104 | + const files = parsedPatches.flatMap((parsedPatch) => | |
| 105 | + options.compactPartialHunkOffsets | |
| 106 | + ? parsedPatch.files.map(compactPartialHunkOffsets) | |
| 107 | + : parsedPatch.files, | |
| 108 | + ); | |
| 69 | 109 | if (files.length > 0) { |
| 70 | 110 | return { kind: "files", files }; |
| 71 | 111 | } |
| @@ -5,10 +5,7 @@ import * as Socket from "effect/unstable/socket/Socket"; · 4 unmodified lines | ||
| 5 | 5 | |
| 6 | 6 | import { remoteHttpClientLayer } from "@t3tools/client-runtime/rpc"; |
| 7 | 7 | import { makeRelayClientTracingLayer } from "@t3tools/shared/relayTracing"; |
| 8 | −import { | |
| 9 | − PrimaryEnvironmentHttpClient, | |
| 10 | − primaryEnvironmentHttpClientLive, | |
| 11 | −} from "../environments/primary/httpClient"; | |
| 8 | +import * as PrimaryEnvironmentHttpClient from "../environments/primary/httpClient"; | |
| 12 | 9 | import { primaryEnvironmentHttpLayer } from "../environments/primary/httpLayer"; |
| 13 | 10 | |
| 14 | 11 | import { browserCryptoLayer } from "../cloud/dpop"; |
| @@ -30,11 +27,11 @@ const relayTracingLayer = makeRelayClientTracingLayer(resolveRelayTracingConfig( · 15 unmodified lines | ||
| 30 | 27 | export const remoteHttpRuntime = ManagedRuntime.make(httpClientLayer); |
| 31 | 28 | |
| 32 | 29 | const primaryHttpRuntime = ManagedRuntime.make( |
| 33 | − primaryEnvironmentHttpClientLive.pipe(Layer.provide(primaryEnvironmentHttpLayer)), | |
| 30 | + PrimaryEnvironmentHttpClient.layer.pipe(Layer.provide(primaryEnvironmentHttpLayer)), | |
| 34 | 31 | ); |
| 35 | 32 | |
| 36 | 33 | export type PrimaryHttpEffectRunner = <A, E>( |
| 37 | − effect: Effect.Effect<A, E, PrimaryEnvironmentHttpClient>, | |
| 34 | + effect: Effect.Effect<A, E, PrimaryEnvironmentHttpClient.PrimaryEnvironmentHttpClient>, | |
| 38 | 35 | ) => Promise<A>; |
| 39 | 36 | |
| 40 | 37 | const livePrimaryHttpRunner: PrimaryHttpEffectRunner = (effect) => |
| @@ -42,8 +39,9 @@ const livePrimaryHttpRunner: PrimaryHttpEffectRunner = (effect) => · 1 unmodified line | ||
| 42 | 39 | |
| 43 | 40 | let primaryHttpRunner = livePrimaryHttpRunner; |
| 44 | 41 | |
| 45 | −export const runPrimaryHttp = <A, E>(effect: Effect.Effect<A, E, PrimaryEnvironmentHttpClient>) => | |
| 46 | − primaryHttpRunner(effect); | |
| 42 | +export const runPrimaryHttp = <A, E>( | |
| 43 | + effect: Effect.Effect<A, E, PrimaryEnvironmentHttpClient.PrimaryEnvironmentHttpClient>, | |
| 44 | +) => primaryHttpRunner(effect); | |
| 47 | 45 | |
| 48 | 46 | export function __setPrimaryHttpRunnerForTests(runner?: PrimaryHttpEffectRunner): void { |
| 49 | 47 | primaryHttpRunner = runner ?? livePrimaryHttpRunner; |
| @@ -6,7 +6,6 @@ import { · 5 unmodified lines | ||
| 6 | 6 | migratePersistedRightPanelState, |
| 7 | 7 | selectActiveRightPanel, |
| 8 | 8 | selectActiveRightPanelSurface, |
| 9 | − selectActiveRightPanelKindWithUrl, | |
| 10 | 9 | selectThreadRightPanelState, |
| 11 | 10 | useRightPanelStore, |
| 12 | 11 | } from "./rightPanelStore"; |
| @@ -254,16 +253,6 @@ describe("rightPanelStore", () => { · 241 unmodified lines | ||
| 254 | 253 | expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBe("plan"); |
| 255 | 254 | }); |
| 256 | 255 | |
| 257 | − it("?diff=1 always wins over persisted state", () => { | |
| 258 | − useRightPanelStore.getState().open(refA, "preview"); | |
| 259 | − expect( | |
| 260 | − selectActiveRightPanelKindWithUrl(useRightPanelStore.getState().byThreadKey, refA, true), | |
| 261 | − ).toBe("diff"); | |
| 262 | − expect( | |
| 263 | − selectActiveRightPanelKindWithUrl(useRightPanelStore.getState().byThreadKey, refA, false), | |
| 264 | − ).toBe("preview"); | |
| 265 | − }); | |
| 266 | − | |
| 267 | 256 | it("removeThread clears persisted state", () => { |
| 268 | 257 | useRightPanelStore.getState().open(refA, "plan"); |
| 269 | 258 | useRightPanelStore.getState().removeThread(refA); |
| @@ -550,13 +550,3 @@ export function selectActiveRightPanelSurface( · 549 unmodified lines | ||
| 550 | 550 | if (!state.isOpen) return null; |
| 551 | 551 | return state.surfaces.find((surface) => surface.id === state.activeSurfaceId) ?? null; |
| 552 | 552 | } |
| 553 | − | |
| 554 | −export function selectActiveRightPanelKindWithUrl( | |
| 555 | − byThreadKey: Record<string, ThreadRightPanelState>, | |
| 556 | − ref: ScopedThreadRef | null | undefined, | |
| 557 | − diffSearchActive: boolean, | |
| 558 | −): RightPanelKind | null { | |
| 559 | − if (!selectThreadRightPanelState(byThreadKey, ref).isOpen) return null; | |
| 560 | − if (diffSearchActive) return "diff"; | |
| 561 | − return selectActiveRightPanel(byThreadKey, ref); | |
| 562 | −} | |
| @@ -1,10 +1,9 @@ | ||
| 1 | −import { createFileRoute, retainSearchParams, useNavigate } from "@tanstack/react-router"; | |
| 1 | +import { createFileRoute, useNavigate } from "@tanstack/react-router"; | |
| 2 | 2 | import { useEffect } from "react"; |
| 3 | 3 | |
| 4 | 4 | import ChatView from "../components/ChatView"; |
| 5 | 5 | import { threadHasStarted } from "../components/ChatView.logic"; |
| 6 | 6 | import { finalizePromotedDraftThreadByRef, useComposerDraftStore } from "../composerDraftStore"; |
| 7 | −import { type DiffRouteSearch, parseDiffRouteSearch } from "../diffRouteSearch"; | |
| 8 | 7 | import { resolveThreadRouteRef } from "../threadRoutes"; |
| 9 | 8 | import { SidebarInset } from "~/components/ui/sidebar"; |
| 10 | 9 | import { useEnvironmentThreadRefs, useThreadDetail, useThreadShell } from "../state/entities"; |
| @@ -74,9 +73,5 @@ function ChatThreadRouteView() { · 63 unmodified lines | ||
| 74 | 73 | } |
| 75 | 74 | |
| 76 | 75 | export const Route = createFileRoute("/_chat/$environmentId/$threadId")({ |
| 77 | − validateSearch: (search) => parseDiffRouteSearch(search), | |
| 78 | − search: { | |
| 79 | − middlewares: [retainSearchParams<DiffRouteSearch>(["diff"])], | |
| 80 | − }, | |
| 81 | 76 | component: ChatThreadRouteView, |
| 82 | 77 | }); |
| @@ -7,7 +7,7 @@ import * as Effect from "effect/Effect"; · 6 unmodified lines | ||
| 7 | 7 | import * as Layer from "effect/Layer"; |
| 8 | 8 | import * as Planetscale from "alchemy/Planetscale"; |
| 9 | 9 | |
| 10 | −import { PlanetscaleDatabase, RelayHyperdrive } from "./src/db.ts"; | |
| 10 | +import * as RelayDb from "./src/db.ts"; | |
| 11 | 11 | import { RelayObservability } from "./src/observability.ts"; |
| 12 | 12 | import { ManagedEndpointZone, RelayApiZone } from "./src/zone.ts"; |
| 13 | 13 | import Api from "./src/worker.ts"; |
| @@ -24,8 +24,8 @@ export default Alchemy.Stack( · 10 unmodified lines | ||
| 24 | 24 | state: Cloudflare.state(), |
| 25 | 25 | }, |
| 26 | 26 | Effect.gen(function* () { |
| 27 | − const db = yield* PlanetscaleDatabase; | |
| 28 | − const hyperdrive = yield* RelayHyperdrive; | |
| 27 | + const db = yield* RelayDb.PlanetscaleDatabase; | |
| 28 | + const hyperdrive = yield* RelayDb.RelayHyperdrive; | |
| 29 | 29 | const managedEndpointZone = yield* ManagedEndpointZone.pipe(Effect.orDie); |
| 30 | 30 | const relayApiZone = yield* RelayApiZone.pipe(Effect.orDie); |
| 31 | 31 | const observability = yield* RelayObservability; |
| @@ -1,4 +1,5 @@ | ||
| 1 | 1 | import * as Context from "effect/Context"; |
| 2 | +import * as Layer from "effect/Layer"; | |
| 2 | 3 | import * as Redacted from "effect/Redacted"; |
| 3 | 4 | import * as Schema from "effect/Schema"; |
| 4 | 5 | |
| @@ -13,20 +14,24 @@ export interface ApnsCredentials { · 8 unmodified lines | ||
| 13 | 14 | readonly environment: ApnsEnvironment; |
| 14 | 15 | } |
| 15 | 16 | |
| 16 | −export interface RelayConfigurationShape { | |
| 17 | − readonly relayIssuer: string; | |
| 18 | − readonly apns: ApnsCredentials; | |
| 19 | − readonly clerkSecretKey: Redacted.Redacted<string>; | |
| 20 | − readonly clerkPublishableKey: string; | |
| 21 | − readonly clerkJwtAudience: string; | |
| 22 | − readonly apnsDeliveryJobSigningSecret: Redacted.Redacted<string>; | |
| 23 | − readonly cloudMintPrivateKey: Redacted.Redacted<string>; | |
| 24 | − readonly cloudMintPublicKey: string; | |
| 25 | − readonly managedEndpointBaseDomain: string | undefined; | |
| 26 | − readonly managedEndpointNamespace: string | undefined; | |
| 27 | −} | |
| 28 | − | |
| 29 | 17 | export class RelayConfiguration extends Context.Service< |
| 30 | 18 | RelayConfiguration, |
| 31 | − RelayConfigurationShape | |
| 19 | + { | |
| 20 | + readonly relayIssuer: string; | |
| 21 | + readonly apns: ApnsCredentials; | |
| 22 | + readonly clerkSecretKey: Redacted.Redacted<string>; | |
| 23 | + readonly clerkPublishableKey: string; | |
| 24 | + readonly clerkJwtAudience: string; | |
| 25 | + readonly apnsDeliveryJobSigningSecret: Redacted.Redacted<string>; | |
| 26 | + readonly cloudMintPrivateKey: Redacted.Redacted<string>; | |
| 27 | + readonly cloudMintPublicKey: string; | |
| 28 | + readonly managedEndpointBaseDomain: string | undefined; | |
| 29 | + readonly managedEndpointNamespace: string | undefined; | |
| 30 | + } | |
| 32 | 31 | >()("t3code-relay/Config/RelayConfiguration") {} |
| 32 | + | |
| 33 | +export const make = (configuration: RelayConfiguration["Service"]) => | |
| 34 | + RelayConfiguration.of(configuration); | |
| 35 | + | |
| 36 | +export const layer = (configuration: RelayConfiguration["Service"]) => | |
| 37 | + Layer.succeed(RelayConfiguration, make(configuration)); | |
| @@ -66,8 +66,8 @@ function makeAgentActivityRows( · 65 unmodified lines | ||
| 66 | 66 | } |
| 67 | 67 | |
| 68 | 68 | function makeEnvironmentLinks( |
| 69 | − overrides: Partial<EnvironmentLinks.EnvironmentLinksShape> = {}, | |
| 70 | −): EnvironmentLinks.EnvironmentLinksShape { | |
| 69 | + overrides: Partial<EnvironmentLinks.EnvironmentLinks["Service"]> = {}, | |
| 70 | +): EnvironmentLinks.EnvironmentLinks["Service"] { | |
| 71 | 71 | return { |
| 72 | 72 | upsert: () => Effect.void, |
| 73 | 73 | listUsersForEnvironment: () => Effect.succeed(["dev:julius"]), |
| @@ -9,7 +9,7 @@ import * as Option from "effect/Option"; · 8 unmodified lines | ||
| 9 | 9 | import * as Schema from "effect/Schema"; |
| 10 | 10 | import { and, desc, eq, isNull } from "drizzle-orm"; |
| 11 | 11 | |
| 12 | −import { RelayDb } from "../db.ts"; | |
| 12 | +import * as RelayDb from "../db.ts"; | |
| 13 | 13 | import { relayAgentActivityRows, relayEnvironmentLinks } from "../persistence/schema.ts"; |
| 14 | 14 | |
| 15 | 15 | export class AgentActivityRowUpsertPersistenceError extends Schema.TaggedErrorClass<AgentActivityRowUpsertPersistenceError>()( |
| @@ -70,7 +70,7 @@ const decodeRelayAgentActivityStateJson = Schema.decodeUnknownOption( · 54 unmodified lines | ||
| 70 | 70 | ); |
| 71 | 71 | |
| 72 | 72 | const make = Effect.gen(function* () { |
| 73 | − const db = yield* RelayDb; | |
| 73 | + const db = yield* RelayDb.RelayDb; | |
| 74 | 74 | |
| 75 | 75 | return AgentActivityRows.of({ |
| 76 | 76 | upsert: Effect.fn("relay.agent_activity_rows.upsert")( |
| @@ -9,7 +9,7 @@ import * as Option from "effect/Option"; · 8 unmodified lines | ||
| 9 | 9 | import * as Redacted from "effect/Redacted"; |
| 10 | 10 | import * as Schema from "effect/Schema"; |
| 11 | 11 | import { Headers, HttpClient, HttpClientRequest } from "effect/unstable/http"; |
| 12 | −import type { ApnsCredentials } from "../Config.ts"; | |
| 12 | +import * as RelayConfiguration from "../Config.ts"; | |
| 13 | 13 | import type { ApnsNotificationPayload } from "./apnsDeliveryJobs.ts"; |
| 14 | 14 | |
| 15 | 15 | const LIVE_ACTIVITY_NAME = "AgentActivity"; |
| @@ -99,9 +99,9 @@ const encodeApnsJwtPayloadJson = Schema.encodeEffect( · 83 unmodified lines | ||
| 99 | 99 | ); |
| 100 | 100 | |
| 101 | 101 | const makeApnsJwt = Effect.fn("relay.apns.make_jwt")(function* (input: { |
| 102 | − readonly teamId: ApnsCredentials["teamId"]; | |
| 103 | − readonly keyId: ApnsCredentials["keyId"]; | |
| 104 | − readonly privateKey: ApnsCredentials["privateKey"]; | |
| 102 | + readonly teamId: RelayConfiguration.ApnsCredentials["teamId"]; | |
| 103 | + readonly keyId: RelayConfiguration.ApnsCredentials["keyId"]; | |
| 104 | + readonly privateKey: RelayConfiguration.ApnsCredentials["privateKey"]; | |
| 105 | 105 | readonly issuedAtUnixSeconds: number; |
| 106 | 106 | }) { |
| 107 | 107 | const headerJson = yield* encodeApnsJwtHeaderJson({ alg: "ES256", kid: input.keyId }).pipe( |
| @@ -235,12 +235,12 @@ export interface ApnsClientShape { · 127 unmodified lines | ||
| 235 | 235 | readonly makeLiveActivityRequest: typeof makeLiveActivityRequest; |
| 236 | 236 | readonly makePushNotificationRequest: typeof makePushNotificationRequest; |
| 237 | 237 | readonly sendLiveActivityRequest: (input: { |
| 238 | − readonly credentials: ApnsCredentials; | |
| 238 | + readonly credentials: RelayConfiguration.ApnsCredentials; | |
| 239 | 239 | readonly request: ApnsLiveActivityRequest; |
| 240 | 240 | readonly issuedAtUnixSeconds: number; |
| 241 | 241 | }) => Effect.Effect<ApnsDeliveryResult, ApnsError>; |
| 242 | 242 | readonly sendPushNotificationRequest: (input: { |
| 243 | − readonly credentials: ApnsCredentials; | |
| 243 | + readonly credentials: RelayConfiguration.ApnsCredentials; | |
| 244 | 244 | readonly request: ApnsPushNotificationRequest; |
| 245 | 245 | readonly issuedAtUnixSeconds: number; |
| 246 | 246 | }) => Effect.Effect<ApnsDeliveryResult, ApnsError>; |
| @@ -153,7 +153,7 @@ function makeLayer(input: { · 152 unmodified lines | ||
| 153 | 153 | Parameters<LiveActivities.LiveActivitiesShape["invalidateDeliveryToken"]>[0] |
| 154 | 154 | >; |
| 155 | 155 | readonly currentTargets?: ReadonlyArray<LiveActivities.TargetRow>; |
| 156 | − readonly config?: RelayConfiguration.RelayConfigurationShape; | |
| 156 | + readonly config?: RelayConfiguration.RelayConfiguration["Service"]; | |
| 157 | 157 | readonly execute?: ( |
| 158 | 158 | request: HttpClientRequest.HttpClientRequest, |
| 159 | 159 | ) => Effect.Effect<HttpClientResponse.HttpClientResponse>; |
| @@ -213,7 +213,7 @@ function makeLayer(input: { · 53 unmodified lines | ||
| 213 | 213 | input.invalidatedTokens?.push(invalidated); |
| 214 | 214 | }), |
| 215 | 215 | }), |
| 216 | − Layer.succeed(RelayConfiguration.RelayConfiguration, input.config ?? config), | |
| 216 | + RelayConfiguration.layer(input.config ?? config), | |
| 217 | 217 | input.execute |
| 218 | 218 | ? Layer.succeed(HttpClient.HttpClient, HttpClient.make(input.execute)) |
| 219 | 219 | : FetchHttpClient.layer, |
| @@ -3,7 +3,7 @@ import { describe, expect, it } from "@effect/vitest"; · 2 unmodified lines | ||
| 3 | 3 | import * as Effect from "effect/Effect"; |
| 4 | 4 | import * as Layer from "effect/Layer"; |
| 5 | 5 | |
| 6 | −import { RelayDb, type RelayDatabase } from "../db.ts"; | |
| 6 | +import * as RelayDb from "../db.ts"; | |
| 7 | 7 | import { relayDeliveryAttempts } from "../persistence/schema.ts"; |
| 8 | 8 | import * as DeliveryAttempts from "./DeliveryAttempts.ts"; |
| 9 | 9 | |
| @@ -20,7 +20,7 @@ describe("DeliveryAttempts", () => { · 10 unmodified lines | ||
| 20 | 20 | }, |
| 21 | 21 | }; |
| 22 | 22 | }, |
| 23 | − } as unknown as RelayDatabase; | |
| 23 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 24 | 24 | |
| 25 | 25 | return Effect.gen(function* () { |
| 26 | 26 | const attempts = yield* DeliveryAttempts.DeliveryAttempts; |
| @@ -52,7 +52,7 @@ describe("DeliveryAttempts", () => { · 25 unmodified lines | ||
| 52 | 52 | Effect.provide( |
| 53 | 53 | DeliveryAttempts.layer.pipe( |
| 54 | 54 | Layer.provide(NodeCryptoLayer.layer), |
| 55 | − Layer.provide(Layer.succeed(RelayDb, fakeDb)), | |
| 55 | + Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)), | |
| 56 | 56 | ), |
| 57 | 57 | ), |
| 58 | 58 | ); |
| @@ -78,7 +78,7 @@ describe("DeliveryAttempts", () => { · 19 unmodified lines | ||
| 78 | 78 | }, |
| 79 | 79 | }; |
| 80 | 80 | }, |
| 81 | − } as unknown as RelayDatabase; | |
| 81 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 82 | 82 | |
| 83 | 83 | return Effect.gen(function* () { |
| 84 | 84 | const attempts = yield* DeliveryAttempts.DeliveryAttempts; |
| @@ -104,7 +104,7 @@ describe("DeliveryAttempts", () => { · 19 unmodified lines | ||
| 104 | 104 | Effect.provide( |
| 105 | 105 | DeliveryAttempts.layer.pipe( |
| 106 | 106 | Layer.provide(NodeCryptoLayer.layer), |
| 107 | − Layer.provide(Layer.succeed(RelayDb, fakeDb)), | |
| 107 | + Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)), | |
| 108 | 108 | ), |
| 109 | 109 | ), |
| 110 | 110 | ); |
| @@ -135,7 +135,7 @@ describe("DeliveryAttempts", () => { · 24 unmodified lines | ||
| 135 | 135 | }), |
| 136 | 136 | }), |
| 137 | 137 | }), |
| 138 | − } as unknown as RelayDatabase; | |
| 138 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 139 | 139 | |
| 140 | 140 | return Effect.gen(function* () { |
| 141 | 141 | const attempts = yield* DeliveryAttempts.DeliveryAttempts; |
| @@ -154,7 +154,7 @@ describe("DeliveryAttempts", () => { · 12 unmodified lines | ||
| 154 | 154 | Effect.provide( |
| 155 | 155 | DeliveryAttempts.layer.pipe( |
| 156 | 156 | Layer.provide(NodeCryptoLayer.layer), |
| 157 | − Layer.provide(Layer.succeed(RelayDb, fakeDb)), | |
| 157 | + Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)), | |
| 158 | 158 | ), |
| 159 | 159 | ), |
| 160 | 160 | ); |
| @@ -185,7 +185,7 @@ describe("DeliveryAttempts", () => { · 24 unmodified lines | ||
| 185 | 185 | }), |
| 186 | 186 | }), |
| 187 | 187 | }), |
| 188 | − } as unknown as RelayDatabase; | |
| 188 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 189 | 189 | |
| 190 | 190 | return Effect.gen(function* () { |
| 191 | 191 | const attempts = yield* DeliveryAttempts.DeliveryAttempts; |
| @@ -204,7 +204,7 @@ describe("DeliveryAttempts", () => { · 12 unmodified lines | ||
| 204 | 204 | Effect.provide( |
| 205 | 205 | DeliveryAttempts.layer.pipe( |
| 206 | 206 | Layer.provide(NodeCryptoLayer.layer), |
| 207 | − Layer.provide(Layer.succeed(RelayDb, fakeDb)), | |
| 207 | + Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)), | |
| 208 | 208 | ), |
| 209 | 209 | ), |
| 210 | 210 | ); |
| @@ -246,7 +246,7 @@ describe("DeliveryAttempts", () => { · 35 unmodified lines | ||
| 246 | 246 | }; |
| 247 | 247 | }, |
| 248 | 248 | }), |
| 249 | − } as unknown as RelayDatabase; | |
| 249 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 250 | 250 | |
| 251 | 251 | return Effect.gen(function* () { |
| 252 | 252 | const attempts = yield* DeliveryAttempts.DeliveryAttempts; |
| @@ -266,7 +266,7 @@ describe("DeliveryAttempts", () => { · 13 unmodified lines | ||
| 266 | 266 | Effect.provide( |
| 267 | 267 | DeliveryAttempts.layer.pipe( |
| 268 | 268 | Layer.provide(NodeCryptoLayer.layer), |
| 269 | − Layer.provide(Layer.succeed(RelayDb, fakeDb)), | |
| 269 | + Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)), | |
| 270 | 270 | ), |
| 271 | 271 | ), |
| 272 | 272 | ); |
| @@ -290,7 +290,7 @@ describe("DeliveryAttempts", () => { · 17 unmodified lines | ||
| 290 | 290 | }, |
| 291 | 291 | }; |
| 292 | 292 | }, |
| 293 | − } as unknown as RelayDatabase; | |
| 293 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 294 | 294 | |
| 295 | 295 | return Effect.gen(function* () { |
| 296 | 296 | const attempts = yield* DeliveryAttempts.DeliveryAttempts; |
| @@ -315,7 +315,7 @@ describe("DeliveryAttempts", () => { · 18 unmodified lines | ||
| 315 | 315 | Effect.provide( |
| 316 | 316 | DeliveryAttempts.layer.pipe( |
| 317 | 317 | Layer.provide(NodeCryptoLayer.layer), |
| 318 | − Layer.provide(Layer.succeed(RelayDb, fakeDb)), | |
| 318 | + Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)), | |
| 319 | 319 | ), |
| 320 | 320 | ), |
| 321 | 321 | ); |
| @@ -7,7 +7,7 @@ import { and, eq, isNull } from "drizzle-orm"; · 6 unmodified lines | ||
| 7 | 7 | import * as Crypto from "effect/Crypto"; |
| 8 | 8 | import * as Schema from "effect/Schema"; |
| 9 | 9 | |
| 10 | −import { RelayDb } from "../db.ts"; | |
| 10 | +import * as RelayDb from "../db.ts"; | |
| 11 | 11 | import { relayDeliveryAttempts } from "../persistence/schema.ts"; |
| 12 | 12 | |
| 13 | 13 | export class DeliveryAttemptRecordPersistenceError extends Schema.TaggedErrorClass<DeliveryAttemptRecordPersistenceError>()( |
| @@ -84,7 +84,7 @@ function insertValues( · 70 unmodified lines | ||
| 84 | 84 | } |
| 85 | 85 | |
| 86 | 86 | const make = Effect.gen(function* () { |
| 87 | − const db = yield* RelayDb; | |
| 87 | + const db = yield* RelayDb.RelayDb; | |
| 88 | 88 | const crypto = yield* Crypto.Crypto; |
| 89 | 89 | |
| 90 | 90 | const isExpiredClaim = (claimedAt: string | null, now: DateTime.DateTime) => { |
| @@ -5,7 +5,7 @@ import { PgDialect } from "drizzle-orm/pg-core"; · 4 unmodified lines | ||
| 5 | 5 | import * as Effect from "effect/Effect"; |
| 6 | 6 | import * as Layer from "effect/Layer"; |
| 7 | 7 | |
| 8 | −import { RelayDb, type RelayDatabase } from "../db.ts"; | |
| 8 | +import * as RelayDb from "../db.ts"; | |
| 9 | 9 | import { relayLiveActivities, relayMobileDevices } from "../persistence/schema.ts"; |
| 10 | 10 | import * as Devices from "./Devices.ts"; |
| 11 | 11 | |
| @@ -71,7 +71,7 @@ describe("Devices", () => { · 59 unmodified lines | ||
| 71 | 71 | }, |
| 72 | 72 | }; |
| 73 | 73 | }, |
| 74 | − } as unknown as RelayDatabase; | |
| 74 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 75 | 75 | |
| 76 | 76 | return Effect.gen(function* () { |
| 77 | 77 | const devices = yield* Devices.Devices; |
| @@ -110,7 +110,9 @@ describe("Devices", () => { · 32 unmodified lines | ||
| 110 | 110 | pushToStartToken: "push-to-start-token", |
| 111 | 111 | }), |
| 112 | 112 | ]); |
| 113 | − }).pipe(Effect.provide(Devices.layer.pipe(Layer.provide(Layer.succeed(RelayDb, fakeDb))))); | |
| 113 | + }).pipe( | |
| 114 | + Effect.provide(Devices.layer.pipe(Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)))), | |
| 115 | + ); | |
| 114 | 116 | }); |
| 115 | 117 | |
| 116 | 118 | it.effect("unregisters APNs state only for the current user device", () => { |
| @@ -130,7 +132,7 @@ describe("Devices", () => { · 13 unmodified lines | ||
| 130 | 132 | }, |
| 131 | 133 | }; |
| 132 | 134 | }, |
| 133 | − } as unknown as RelayDatabase; | |
| 135 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 134 | 136 | |
| 135 | 137 | return Effect.gen(function* () { |
| 136 | 138 | const devices = yield* Devices.Devices; |
| @@ -156,7 +158,9 @@ describe("Devices", () => { · 19 unmodified lines | ||
| 156 | 158 | params: ["user-2", "device-1"], |
| 157 | 159 | }, |
| 158 | 160 | ]); |
| 159 | − }).pipe(Effect.provide(Devices.layer.pipe(Layer.provide(Layer.succeed(RelayDb, fakeDb))))); | |
| 161 | + }).pipe( | |
| 162 | + Effect.provide(Devices.layer.pipe(Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)))), | |
| 163 | + ); | |
| 160 | 164 | }); |
| 161 | 165 | |
| 162 | 166 | it.effect("lists safe notification state without exposing APNs tokens", () => { |
| @@ -184,7 +188,7 @@ describe("Devices", () => { · 21 unmodified lines | ||
| 184 | 188 | }; |
| 185 | 189 | }, |
| 186 | 190 | }), |
| 187 | − } as unknown as RelayDatabase; | |
| 191 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 188 | 192 | |
| 189 | 193 | return Effect.gen(function* () { |
| 190 | 194 | const devices = yield* Devices.Devices; |
| @@ -215,6 +219,8 @@ describe("Devices", () => { · 24 unmodified lines | ||
| 215 | 219 | updatedAt: "2026-06-01T00:00:00.000Z", |
| 216 | 220 | }, |
| 217 | 221 | ]); |
| 218 | − }).pipe(Effect.provide(Devices.layer.pipe(Layer.provide(Layer.succeed(RelayDb, fakeDb))))); | |
| 222 | + }).pipe( | |
| 223 | + Effect.provide(Devices.layer.pipe(Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)))), | |
| 224 | + ); | |
| 219 | 225 | }); |
| 220 | 226 | }); |
| @@ -10,7 +10,7 @@ import * as Schema from "effect/Schema"; · 9 unmodified lines | ||
| 10 | 10 | import { and, eq } from "drizzle-orm"; |
| 11 | 11 | import { sql } from "drizzle-orm"; |
| 12 | 12 | |
| 13 | −import { RelayDb } from "../db.ts"; | |
| 13 | +import * as RelayDb from "../db.ts"; | |
| 14 | 14 | import { relayLiveActivities, relayMobileDevices } from "../persistence/schema.ts"; |
| 15 | 15 | |
| 16 | 16 | export class DeviceRegistrationPersistenceError extends Schema.TaggedErrorClass<DeviceRegistrationPersistenceError>()( |
| @@ -59,7 +59,7 @@ export class Devices extends Context.Service<Devices, DevicesShape>()( · 42 unmodified lines | ||
| 59 | 59 | ) {} |
| 60 | 60 | |
| 61 | 61 | const make = Effect.gen(function* () { |
| 62 | − const db = yield* RelayDb; | |
| 62 | + const db = yield* RelayDb.RelayDb; | |
| 63 | 63 | |
| 64 | 64 | return Devices.of({ |
| 65 | 65 | register: Effect.fn("relay.devices.register")( |
| @@ -8,7 +8,7 @@ import { PgDialect } from "drizzle-orm/pg-core"; · 7 unmodified lines | ||
| 8 | 8 | import * as Effect from "effect/Effect"; |
| 9 | 9 | import * as Layer from "effect/Layer"; |
| 10 | 10 | |
| 11 | −import { RelayDb, type RelayDatabase } from "../db.ts"; | |
| 11 | +import * as RelayDb from "../db.ts"; | |
| 12 | 12 | import { relayLiveActivities } from "../persistence/schema.ts"; |
| 13 | 13 | import * as LiveActivities from "./LiveActivities.ts"; |
| 14 | 14 | |
| @@ -88,7 +88,7 @@ describe("LiveActivities", () => { · 73 unmodified lines | ||
| 88 | 88 | }, |
| 89 | 89 | }; |
| 90 | 90 | }, |
| 91 | − } as unknown as RelayDatabase; | |
| 91 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 92 | 92 | |
| 93 | 93 | return Effect.gen(function* () { |
| 94 | 94 | const liveActivities = yield* LiveActivities.LiveActivities; |
| @@ -138,7 +138,9 @@ describe("LiveActivities", () => { · 43 unmodified lines | ||
| 138 | 138 | }), |
| 139 | 139 | ); |
| 140 | 140 | }).pipe( |
| 141 | − Effect.provide(LiveActivities.layer.pipe(Layer.provide(Layer.succeed(RelayDb, fakeDb)))), | |
| 141 | + Effect.provide( | |
| 142 | + LiveActivities.layer.pipe(Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb))), | |
| 143 | + ), | |
| 142 | 144 | ); |
| 143 | 145 | }, |
| 144 | 146 | ); |
| @@ -164,7 +166,7 @@ describe("LiveActivities", () => { · 19 unmodified lines | ||
| 164 | 166 | }, |
| 165 | 167 | }; |
| 166 | 168 | }, |
| 167 | − } as unknown as RelayDatabase; | |
| 169 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 168 | 170 | |
| 169 | 171 | return Effect.gen(function* () { |
| 170 | 172 | const liveActivities = yield* LiveActivities.LiveActivities; |
| @@ -190,7 +192,9 @@ describe("LiveActivities", () => { · 19 unmodified lines | ||
| 190 | 192 | }), |
| 191 | 193 | ); |
| 192 | 194 | }).pipe( |
| 193 | − Effect.provide(LiveActivities.layer.pipe(Layer.provide(Layer.succeed(RelayDb, fakeDb)))), | |
| 195 | + Effect.provide( | |
| 196 | + LiveActivities.layer.pipe(Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb))), | |
| 197 | + ), | |
| 194 | 198 | ); |
| 195 | 199 | }); |
| 196 | 200 | }); |
| @@ -12,7 +12,7 @@ import * as Layer from "effect/Layer"; · 11 unmodified lines | ||
| 12 | 12 | import * as Schema from "effect/Schema"; |
| 13 | 13 | import { and, eq, sql } from "drizzle-orm"; |
| 14 | 14 | |
| 15 | −import { RelayDb } from "../db.ts"; | |
| 15 | +import * as RelayDb from "../db.ts"; | |
| 16 | 16 | import { relayLiveActivities, relayMobileDevices } from "../persistence/schema.ts"; |
| 17 | 17 | |
| 18 | 18 | export class LiveActivityRegistrationPersistenceError extends Schema.TaggedErrorClass<LiveActivityRegistrationPersistenceError>()( |
| @@ -108,7 +108,7 @@ const encodeRelayAgentActivityAggregateStateJson = Schema.encodeEffect( · 89 unmodified lines | ||
| 108 | 108 | ); |
| 109 | 109 | |
| 110 | 110 | const make = Effect.gen(function* () { |
| 111 | − const db = yield* RelayDb; | |
| 111 | + const db = yield* RelayDb.RelayDb; | |
| 112 | 112 | |
| 113 | 113 | return LiveActivities.of({ |
| 114 | 114 | register: Effect.fn("relay.live_activities.register")( |
| @@ -86,8 +86,8 @@ function makeAgentActivityRows( · 85 unmodified lines | ||
| 86 | 86 | } |
| 87 | 87 | |
| 88 | 88 | function makeEnvironmentLinks( |
| 89 | − overrides: Partial<EnvironmentLinks.EnvironmentLinksShape> = {}, | |
| 90 | −): EnvironmentLinks.EnvironmentLinksShape { | |
| 89 | + overrides: Partial<EnvironmentLinks.EnvironmentLinks["Service"]> = {}, | |
| 90 | +): EnvironmentLinks.EnvironmentLinks["Service"] { | |
| 91 | 91 | return { |
| 92 | 92 | upsert: () => Effect.void, |
| 93 | 93 | listUsersForEnvironment: () => Effect.succeed(["dev:julius"]), |
| @@ -153,7 +153,7 @@ function makeRegistrationReplayLayer(input: { · 59 unmodified lines | ||
| 153 | 153 | Layer.succeed(EnvironmentLinks.EnvironmentLinks, makeEnvironmentLinks()), |
| 154 | 154 | Layer.succeed(LiveActivities.LiveActivities, input.liveActivities), |
| 155 | 155 | Layer.succeed(DeliveryAttempts.DeliveryAttempts, makeDeliveryAttempts()), |
| 156 | − Layer.succeed(RelayConfiguration.RelayConfiguration, config), | |
| 156 | + RelayConfiguration.layer(config), | |
| 157 | 157 | Layer.succeed(ApnsDeliveryQueue.ApnsDeliveryQueueSender, { |
| 158 | 158 | send: (body) => |
| 159 | 159 | Effect.sync(() => { |
| @@ -4,7 +4,7 @@ import * as Effect from "effect/Effect"; · 3 unmodified lines | ||
| 4 | 4 | import * as Layer from "effect/Layer"; |
| 5 | 5 | import * as Option from "effect/Option"; |
| 6 | 6 | |
| 7 | −import { RelayDb, type RelayDatabase } from "../db.ts"; | |
| 7 | +import * as RelayDb from "../db.ts"; | |
| 8 | 8 | import { relayDpopProofs } from "../persistence/schema.ts"; |
| 9 | 9 | import * as DpopProofs from "./DpopProofs.ts"; |
| 10 | 10 | |
| @@ -41,7 +41,7 @@ describe("DpopProofReplay", () => { · 30 unmodified lines | ||
| 41 | 41 | }, |
| 42 | 42 | }; |
| 43 | 43 | }, |
| 44 | − } as unknown as RelayDatabase; | |
| 44 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 45 | 45 | |
| 46 | 46 | return Effect.gen(function* () { |
| 47 | 47 | const replay = yield* DpopProofs.DpopProofReplay; |
| @@ -67,7 +67,9 @@ describe("DpopProofReplay", () => { · 19 unmodified lines | ||
| 67 | 67 | expiresAt: "2026-05-25T12:00:00.000Z", |
| 68 | 68 | }, |
| 69 | 69 | ]); |
| 70 | − }).pipe(Effect.provide(DpopProofs.layer.pipe(Layer.provide(Layer.succeed(RelayDb, fakeDb))))); | |
| 70 | + }).pipe( | |
| 71 | + Effect.provide(DpopProofs.layer.pipe(Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)))), | |
| 72 | + ); | |
| 71 | 73 | }); |
| 72 | 74 | |
| 73 | 75 | it.effect("prunes expired proof rows from the maintenance path", () => { |
| @@ -84,12 +86,14 @@ describe("DpopProofReplay", () => { · 10 unmodified lines | ||
| 84 | 86 | }, |
| 85 | 87 | }; |
| 86 | 88 | }, |
| 87 | − } as unknown as RelayDatabase; | |
| 89 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 88 | 90 | |
| 89 | 91 | return Effect.gen(function* () { |
| 90 | 92 | const replay = yield* DpopProofs.DpopProofReplay; |
| 91 | 93 | yield* replay.pruneExpired; |
| 92 | 94 | expect(calls).toEqual(["delete", "delete.where"]); |
| 93 | − }).pipe(Effect.provide(DpopProofs.layer.pipe(Layer.provide(Layer.succeed(RelayDb, fakeDb))))); | |
| 95 | + }).pipe( | |
| 96 | + Effect.provide(DpopProofs.layer.pipe(Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)))), | |
| 97 | + ); | |
| 94 | 98 | }); |
| 95 | 99 | }); |
| @@ -7,7 +7,7 @@ import * as HttpApiError from "effect/unstable/httpapi/HttpApiError"; · 6 unmodified lines | ||
| 7 | 7 | import { lt } from "drizzle-orm"; |
| 8 | 8 | |
| 9 | 9 | import { verifyDpopProof } from "@t3tools/shared/dpop"; |
| 10 | −import { RelayDb } from "../db.ts"; | |
| 10 | +import * as RelayDb from "../db.ts"; | |
| 11 | 11 | import { relayDpopProofs } from "../persistence/schema.ts"; |
| 12 | 12 | |
| 13 | 13 | export class DpopProofReplayPersistenceError extends Schema.TaggedErrorClass<DpopProofReplayPersistenceError>()( |
| @@ -21,34 +21,31 @@ export class DpopProofReplayPersistenceError extends Schema.TaggedErrorClass<Dpo · 7 unmodified lines | ||
| 21 | 21 | } |
| 22 | 22 | } |
| 23 | 23 | |
| 24 | −export interface DpopProofReplayShape { | |
| 25 | − readonly verifyAndConsume: (input: { | |
| 26 | − readonly proof: string | undefined; | |
| 27 | − readonly method: string; | |
| 28 | − readonly url: string; | |
| 29 | − readonly expectedThumbprint?: string; | |
| 30 | − readonly expectedAccessToken?: string; | |
| 31 | − readonly now: DateTime.DateTime; | |
| 32 | − }) => Effect.Effect<string, HttpApiError.Unauthorized | DpopProofReplayPersistenceError>; | |
| 33 | − | |
| 34 | − readonly consume: (input: { | |
| 35 | − readonly thumbprint: string; | |
| 36 | − readonly jti: string; | |
| 37 | − readonly iat: number; | |
| 38 | − readonly expiresAt: DateTime.DateTime; | |
| 39 | − }) => Effect.Effect<boolean, DpopProofReplayPersistenceError>; | |
| 40 | − | |
| 41 | − readonly pruneExpired: Effect.Effect<void, DpopProofReplayPersistenceError>; | |
| 42 | −} | |
| 43 | − | |
| 44 | −export class DpopProofReplay extends Context.Service<DpopProofReplay, DpopProofReplayShape>()( | |
| 45 | − "t3code-relay/auth/DpopProofs/DpopProofReplay", | |
| 46 | −) {} | |
| 24 | +export class DpopProofReplay extends Context.Service< | |
| 25 | + DpopProofReplay, | |
| 26 | + { | |
| 27 | + readonly verifyAndConsume: (input: { | |
| 28 | + readonly proof: string | undefined; | |
| 29 | + readonly method: string; | |
| 30 | + readonly url: string; | |
| 31 | + readonly expectedThumbprint?: string; | |
| 32 | + readonly expectedAccessToken?: string; | |
| 33 | + readonly now: DateTime.DateTime; | |
| 34 | + }) => Effect.Effect<string, HttpApiError.Unauthorized | DpopProofReplayPersistenceError>; | |
| 35 | + readonly consume: (input: { | |
| 36 | + readonly thumbprint: string; | |
| 37 | + readonly jti: string; | |
| 38 | + readonly iat: number; | |
| 39 | + readonly expiresAt: DateTime.DateTime; | |
| 40 | + }) => Effect.Effect<boolean, DpopProofReplayPersistenceError>; | |
| 41 | + readonly pruneExpired: Effect.Effect<void, DpopProofReplayPersistenceError>; | |
| 42 | + } | |
| 43 | +>()("t3code-relay/auth/DpopProofs/DpopProofReplay") {} | |
| 47 | 44 | |
| 48 | 45 | const make = Effect.gen(function* () { |
| 49 | − const db = yield* RelayDb; | |
| 46 | + const db = yield* RelayDb.RelayDb; | |
| 50 | 47 | |
| 51 | − const consume: DpopProofReplayShape["consume"] = Effect.fn("relay.dpop_proofs.consume")( | |
| 48 | + const consume: DpopProofReplay["Service"]["consume"] = Effect.fn("relay.dpop_proofs.consume")( | |
| 52 | 49 | function* (input) { |
| 53 | 50 | const createdAt = DateTime.formatIso(yield* DateTime.now); |
| 54 | 51 | const inserted = yield* db |
| @@ -67,7 +64,7 @@ const make = Effect.gen(function* () { · 12 unmodified lines | ||
| 67 | 64 | Effect.mapError((cause) => new DpopProofReplayPersistenceError({ cause })), |
| 68 | 65 | ); |
| 69 | 66 | |
| 70 | − const verifyAndConsume: DpopProofReplayShape["verifyAndConsume"] = Effect.fn( | |
| 67 | + const verifyAndConsume: DpopProofReplay["Service"]["verifyAndConsume"] = Effect.fn( | |
| 71 | 68 | "relay.dpop_proofs.verify_and_consume", |
| 72 | 69 | )(function* (input) { |
| 73 | 70 | yield* Effect.annotateCurrentSpan({ |
| @@ -114,7 +111,7 @@ const make = Effect.gen(function* () { · 40 unmodified lines | ||
| 114 | 111 | return result.thumbprint; |
| 115 | 112 | }); |
| 116 | 113 | |
| 117 | − const pruneExpired: DpopProofReplayShape["pruneExpired"] = Effect.gen(function* () { | |
| 114 | + const pruneExpired: DpopProofReplay["Service"]["pruneExpired"] = Effect.gen(function* () { | |
| 118 | 115 | const now = DateTime.formatIso(yield* DateTime.now); |
| 119 | 116 | yield* Effect.annotateCurrentSpan({ "relay.dpop_prune.before": now }); |
| 120 | 117 | yield* db.delete(relayDpopProofs).where(lt(relayDpopProofs.expiresAt, now)); |
| @@ -10,7 +10,7 @@ import * as DateTime from "effect/DateTime"; · 9 unmodified lines | ||
| 10 | 10 | import * as Effect from "effect/Effect"; |
| 11 | 11 | import * as Layer from "effect/Layer"; |
| 12 | 12 | |
| 13 | −import { RelayDb, type RelayDatabase } from "../db.ts"; | |
| 13 | +import * as RelayDb from "../db.ts"; | |
| 14 | 14 | import { relayDpopProofs } from "../persistence/schema.ts"; |
| 15 | 15 | import * as DpopProofs from "./DpopProofs.ts"; |
| 16 | 16 | |
| @@ -78,8 +78,8 @@ function layer( · 61 unmodified lines | ||
| 78 | 78 | }), |
| 79 | 79 | }; |
| 80 | 80 | }, |
| 81 | − } as unknown as RelayDatabase; | |
| 82 | − return DpopProofs.layer.pipe(Layer.provide(Layer.succeed(RelayDb, fakeDb))); | |
| 81 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 82 | + return DpopProofs.layer.pipe(Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb))); | |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | 85 | function consumeEachProofOnce() { |
| @@ -33,9 +33,7 @@ const config = RelayConfiguration.RelayConfiguration.of({ · 32 unmodified lines | ||
| 33 | 33 | managedEndpointNamespace: undefined, |
| 34 | 34 | }); |
| 35 | 35 | |
| 36 | −const layer = RelayTokens.layer.pipe( | |
| 37 | − Layer.provide(Layer.succeed(RelayConfiguration.RelayConfiguration, config)), | |
| 38 | −); | |
| 36 | +const layer = RelayTokens.layer.pipe(Layer.provide(RelayConfiguration.layer(config))); | |
| 39 | 37 | |
| 40 | 38 | describe("RelayTokens", () => { |
| 41 | 39 | it.effect("issues a user-bound environment link challenge", () => |
| @@ -93,45 +93,44 @@ function resolveDpopAccessTokenScopes(input: { · 92 unmodified lines | ||
| 93 | 93 | }); |
| 94 | 94 | } |
| 95 | 95 | |
| 96 | −export interface RelayTokensShape { | |
| 97 | − readonly resolveDpopAccessTokenScopes: typeof resolveDpopAccessTokenScopes; | |
| 98 | − readonly issueLinkChallenge: (input: { | |
| 99 | − readonly userId: string; | |
| 100 | − readonly request: RelayEnvironmentLinkChallengeRequest; | |
| 101 | − readonly jti: string; | |
| 102 | − readonly issuedAtEpochSeconds: number; | |
| 103 | − readonly expiresAtEpochSeconds: number; | |
| 104 | − }) => Effect.Effect<string, RelayJwtError>; | |
| 105 | − readonly verifyLinkChallenge: (input: { | |
| 106 | − readonly token: string; | |
| 107 | − readonly userId: string; | |
| 108 | − readonly request: RelayEnvironmentLinkChallengeRequest; | |
| 109 | − readonly nowEpochSeconds: number; | |
| 110 | − }) => Effect.Effect<LinkChallengeClaims | null>; | |
| 111 | − readonly issueDpopAccessToken: (input: { | |
| 112 | − readonly userId: string; | |
| 113 | − readonly proofKeyThumbprint: string; | |
| 114 | − readonly jti: string; | |
| 115 | − readonly issuedAtEpochSeconds: number; | |
| 116 | − readonly expiresAtEpochSeconds: number; | |
| 117 | − readonly clientId: RelayPublicClientId; | |
| 118 | − readonly scopes: ReadonlyArray<RelayDpopAccessTokenScope>; | |
| 119 | − }) => Effect.Effect<string, RelayJwtError>; | |
| 120 | − readonly verifyDpopAccessToken: (input: { | |
| 121 | − readonly token: string; | |
| 122 | − readonly nowEpochSeconds: number; | |
| 123 | − }) => Effect.Effect<RelayDpopAccessTokenClaims | null>; | |
| 124 | −} | |
| 125 | − | |
| 126 | −export class RelayTokens extends Context.Service<RelayTokens, RelayTokensShape>()( | |
| 127 | − "t3code-relay/auth/RelayTokens", | |
| 128 | −) {} | |
| 96 | +export class RelayTokens extends Context.Service< | |
| 97 | + RelayTokens, | |
| 98 | + { | |
| 99 | + readonly resolveDpopAccessTokenScopes: typeof resolveDpopAccessTokenScopes; | |
| 100 | + readonly issueLinkChallenge: (input: { | |
| 101 | + readonly userId: string; | |
| 102 | + readonly request: RelayEnvironmentLinkChallengeRequest; | |
| 103 | + readonly jti: string; | |
| 104 | + readonly issuedAtEpochSeconds: number; | |
| 105 | + readonly expiresAtEpochSeconds: number; | |
| 106 | + }) => Effect.Effect<string, RelayJwtError>; | |
| 107 | + readonly verifyLinkChallenge: (input: { | |
| 108 | + readonly token: string; | |
| 109 | + readonly userId: string; | |
| 110 | + readonly request: RelayEnvironmentLinkChallengeRequest; | |
| 111 | + readonly nowEpochSeconds: number; | |
| 112 | + }) => Effect.Effect<LinkChallengeClaims | null>; | |
| 113 | + readonly issueDpopAccessToken: (input: { | |
| 114 | + readonly userId: string; | |
| 115 | + readonly proofKeyThumbprint: string; | |
| 116 | + readonly jti: string; | |
| 117 | + readonly issuedAtEpochSeconds: number; | |
| 118 | + readonly expiresAtEpochSeconds: number; | |
| 119 | + readonly clientId: RelayPublicClientId; | |
| 120 | + readonly scopes: ReadonlyArray<RelayDpopAccessTokenScope>; | |
| 121 | + }) => Effect.Effect<string, RelayJwtError>; | |
| 122 | + readonly verifyDpopAccessToken: (input: { | |
| 123 | + readonly token: string; | |
| 124 | + readonly nowEpochSeconds: number; | |
| 125 | + }) => Effect.Effect<RelayDpopAccessTokenClaims | null>; | |
| 126 | + } | |
| 127 | +>()("t3code-relay/auth/RelayTokens") {} | |
| 129 | 128 | |
| 130 | 129 | const make = Effect.gen(function* () { |
| 131 | 130 | const config = yield* RelayConfiguration.RelayConfiguration; |
| 132 | 131 | const issuer = normalizeRelayIssuer(config.relayIssuer); |
| 133 | 132 | |
| 134 | − const issueLinkChallenge: RelayTokensShape["issueLinkChallenge"] = Effect.fn( | |
| 133 | + const issueLinkChallenge: RelayTokens["Service"]["issueLinkChallenge"] = Effect.fn( | |
| 135 | 134 | "relay.tokens.issue_link_challenge", |
| 136 | 135 | )(function* (input) { |
| 137 | 136 | return yield* signRelayJwt({ |
| @@ -150,7 +149,7 @@ const make = Effect.gen(function* () { · 12 unmodified lines | ||
| 150 | 149 | }); |
| 151 | 150 | }); |
| 152 | 151 | |
| 153 | − const verifyLinkChallenge: RelayTokensShape["verifyLinkChallenge"] = Effect.fn( | |
| 152 | + const verifyLinkChallenge: RelayTokens["Service"]["verifyLinkChallenge"] = Effect.fn( | |
| 154 | 153 | "relay.tokens.verify_link_challenge", |
| 155 | 154 | )((input) => |
| 156 | 155 | verifyRelayJwt({ |
| @@ -177,7 +176,7 @@ const make = Effect.gen(function* () { · 20 unmodified lines | ||
| 177 | 176 | ), |
| 178 | 177 | ); |
| 179 | 178 | |
| 180 | − const issueDpopAccessToken: RelayTokensShape["issueDpopAccessToken"] = Effect.fn( | |
| 179 | + const issueDpopAccessToken: RelayTokens["Service"]["issueDpopAccessToken"] = Effect.fn( | |
| 181 | 180 | "relay.tokens.issue_dpop_access_token", |
| 182 | 181 | )(function* (input) { |
| 183 | 182 | return yield* signRelayJwt({ |
| @@ -197,7 +196,7 @@ const make = Effect.gen(function* () { · 13 unmodified lines | ||
| 197 | 196 | }); |
| 198 | 197 | }); |
| 199 | 198 | |
| 200 | − const verifyDpopAccessToken: RelayTokensShape["verifyDpopAccessToken"] = Effect.fn( | |
| 199 | + const verifyDpopAccessToken: RelayTokens["Service"]["verifyDpopAccessToken"] = Effect.fn( | |
| 201 | 200 | "relay.tokens.verify_dpop_access_token", |
| 202 | 201 | )((input) => |
| 203 | 202 | verifyRelayJwt({ |
| @@ -10,11 +10,12 @@ import * as Effect from "effect/Effect"; · 9 unmodified lines | ||
| 10 | 10 | |
| 11 | 11 | import { relayDatabaseMode } from "./dbConfig.ts"; |
| 12 | 12 | |
| 13 | −export interface RelayDatabase extends EffectPgDatabase { | |
| 14 | − readonly $client: PgClient; | |
| 15 | −} | |
| 16 | − | |
| 17 | −export class RelayDb extends Context.Service<RelayDb, RelayDatabase>()("t3code-relay/db/RelayDb") {} | |
| 13 | +export class RelayDb extends Context.Service< | |
| 14 | + RelayDb, | |
| 15 | + EffectPgDatabase & { | |
| 16 | + readonly $client: PgClient; | |
| 17 | + } | |
| 18 | +>()("t3code-relay/db/RelayDb") {} | |
| 18 | 19 | |
| 19 | 20 | export const PlanetscaleDatabase = Effect.gen(function* () { |
| 20 | 21 | const { stage } = yield* Alchemy.Stack; |
| @@ -161,8 +161,8 @@ function connectorTestLayer( · 160 unmodified lines | ||
| 161 | 161 | request: HttpClientRequest.HttpClientRequest, |
| 162 | 162 | ) => Effect.Effect<HttpClientResponse.HttpClientResponse>, |
| 163 | 163 | options?: { |
| 164 | − readonly links?: EnvironmentLinks.EnvironmentLinksShape; | |
| 165 | − readonly allocations?: ManagedEndpointAllocations.ManagedEndpointAllocationsShape; | |
| 164 | + readonly links?: EnvironmentLinks.EnvironmentLinks["Service"]; | |
| 165 | + readonly allocations?: ManagedEndpointAllocations.ManagedEndpointAllocations["Service"]; | |
| 166 | 166 | }, |
| 167 | 167 | ) { |
| 168 | 168 | return EnvironmentConnector.layer.pipe( |
| @@ -174,7 +174,7 @@ function connectorTestLayer( · 5 unmodified lines | ||
| 174 | 174 | options?.allocations ?? makeAllocations(), |
| 175 | 175 | ), |
| 176 | 176 | ), |
| 177 | − Layer.provide(Layer.succeed(RelayConfiguration.RelayConfiguration, settings)), | |
| 177 | + Layer.provide(RelayConfiguration.layer(settings)), | |
| 178 | 178 | Layer.provide(Layer.succeed(HttpClient.HttpClient, HttpClient.make(execute))), |
| 179 | 179 | ); |
| 180 | 180 | } |
| @@ -189,7 +189,7 @@ function makeAllocations( · 8 unmodified lines | ||
| 189 | 189 | dnsRecordId: "dns-record-id", |
| 190 | 190 | readyAt: "2026-05-25T00:00:00.000Z", |
| 191 | 191 | }, |
| 192 | −): ManagedEndpointAllocations.ManagedEndpointAllocationsShape { | |
| 192 | +): ManagedEndpointAllocations.ManagedEndpointAllocations["Service"] { | |
| 193 | 193 | return { |
| 194 | 194 | get: () => Effect.succeed(allocation), |
| 195 | 195 | reserve: () => Effect.die("unused"), |
| @@ -202,7 +202,7 @@ function makeAllocations( · 6 unmodified lines | ||
| 202 | 202 | |
| 203 | 203 | function makeLinks( |
| 204 | 204 | overrides: Partial<EnvironmentLinks.RelayLinkedEnvironmentRecord> = {}, |
| 205 | −): EnvironmentLinks.EnvironmentLinksShape { | |
| 205 | +): EnvironmentLinks.EnvironmentLinks["Service"] { | |
| 206 | 206 | return { |
| 207 | 207 | upsert: () => Effect.void, |
| 208 | 208 | listUsersForEnvironment: () => Effect.succeed([]), |
| @@ -35,7 +35,9 @@ import * as Layer from "effect/Layer"; · 34 unmodified lines | ||
| 35 | 35 | import * as Option from "effect/Option"; |
| 36 | 36 | import * as Redacted from "effect/Redacted"; |
| 37 | 37 | import * as Schema from "effect/Schema"; |
| 38 | −import { FetchHttpClient, HttpClient, HttpClientError } from "effect/unstable/http"; | |
| 38 | +import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient"; | |
| 39 | +import * as HttpClient from "effect/unstable/http/HttpClient"; | |
| 40 | +import * as HttpClientError from "effect/unstable/http/HttpClientError"; | |
| 39 | 41 | |
| 40 | 42 | import * as EnvironmentLinks from "./EnvironmentLinks.ts"; |
| 41 | 43 | import * as ManagedEndpointAllocations from "./ManagedEndpointAllocations.ts"; |
| @@ -139,22 +141,20 @@ export type EnvironmentConnectorError = · 97 unmodified lines | ||
| 139 | 141 | export const ENVIRONMENT_MINT_REQUEST_TIMEOUT_MS = 10_000; |
| 140 | 142 | const ENVIRONMENT_HEALTH_CLOCK_SKEW_MILLIS = 60 * 1_000; |
| 141 | 143 | |
| 142 | −export interface EnvironmentConnectorShape { | |
| 143 | − readonly connect: (input: { | |
| 144 | − readonly userId: string; | |
| 145 | − readonly environmentId: string; | |
| 146 | − readonly clientProofKeyThumbprint: string; | |
| 147 | − readonly deviceId?: string; | |
| 148 | − }) => Effect.Effect<RelayEnvironmentConnectResponse, EnvironmentConnectorError>; | |
| 149 | − readonly status: (input: { | |
| 150 | − readonly userId: string; | |
| 151 | − readonly environmentId: string; | |
| 152 | − }) => Effect.Effect<RelayEnvironmentStatusResponse, EnvironmentConnectorError>; | |
| 153 | −} | |
| 154 | − | |
| 155 | 144 | export class EnvironmentConnector extends Context.Service< |
| 156 | 145 | EnvironmentConnector, |
| 157 | − EnvironmentConnectorShape | |
| 146 | + { | |
| 147 | + readonly connect: (input: { | |
| 148 | + readonly userId: string; | |
| 149 | + readonly environmentId: string; | |
| 150 | + readonly clientProofKeyThumbprint: string; | |
| 151 | + readonly deviceId?: string; | |
| 152 | + }) => Effect.Effect<RelayEnvironmentConnectResponse, EnvironmentConnectorError>; | |
| 153 | + readonly status: (input: { | |
| 154 | + readonly userId: string; | |
| 155 | + readonly environmentId: string; | |
| 156 | + }) => Effect.Effect<RelayEnvironmentStatusResponse, EnvironmentConnectorError>; | |
| 157 | + } | |
| 158 | 158 | >()("t3code-relay/environments/EnvironmentConnector") {} |
| 159 | 159 | |
| 160 | 160 | const decodeMintResponseProof = Schema.decodeUnknownEffect( |
| @@ -4,7 +4,7 @@ import { PgDialect, QueryBuilder } from "drizzle-orm/pg-core"; · 3 unmodified lines | ||
| 4 | 4 | import * as Effect from "effect/Effect"; |
| 5 | 5 | import * as Layer from "effect/Layer"; |
| 6 | 6 | |
| 7 | −import { RelayDb, type RelayDatabase } from "../db.ts"; | |
| 7 | +import * as RelayDb from "../db.ts"; | |
| 8 | 8 | import { relayEnvironmentCredentials } from "../persistence/schema.ts"; |
| 9 | 9 | import * as EnvironmentCredentials from "./EnvironmentCredentials.ts"; |
| 10 | 10 | |
| @@ -47,7 +47,7 @@ describe("EnvironmentCredentials", () => { · 36 unmodified lines | ||
| 47 | 47 | }), |
| 48 | 48 | }; |
| 49 | 49 | }, |
| 50 | − } as unknown as RelayDatabase; | |
| 50 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 51 | 51 | |
| 52 | 52 | return Effect.gen(function* () { |
| 53 | 53 | const credentials = yield* EnvironmentCredentials.EnvironmentCredentials; |
| @@ -87,7 +87,7 @@ describe("EnvironmentCredentials", () => { · 33 unmodified lines | ||
| 87 | 87 | Effect.provide( |
| 88 | 88 | EnvironmentCredentials.layer.pipe( |
| 89 | 89 | Layer.provide(NodeCryptoLayer.layer), |
| 90 | − Layer.provide(Layer.succeed(RelayDb, fakeDb)), | |
| 90 | + Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)), | |
| 91 | 91 | ), |
| 92 | 92 | ), |
| 93 | 93 | ); |
| @@ -118,7 +118,7 @@ describe("EnvironmentCredentials", () => { · 24 unmodified lines | ||
| 118 | 118 | }, |
| 119 | 119 | }; |
| 120 | 120 | }, |
| 121 | − } as unknown as RelayDatabase; | |
| 121 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 122 | 122 | |
| 123 | 123 | return Effect.gen(function* () { |
| 124 | 124 | const credentials = yield* EnvironmentCredentials.EnvironmentCredentials; |
| @@ -150,7 +150,7 @@ describe("EnvironmentCredentials", () => { · 25 unmodified lines | ||
| 150 | 150 | Effect.provide( |
| 151 | 151 | EnvironmentCredentials.layer.pipe( |
| 152 | 152 | Layer.provide(NodeCryptoLayer.layer), |
| 153 | − Layer.provide(Layer.succeed(RelayDb, fakeDb)), | |
| 153 | + Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)), | |
| 154 | 154 | ), |
| 155 | 155 | ), |
| 156 | 156 | ); |
| @@ -8,7 +8,7 @@ import * as Option from "effect/Option"; · 7 unmodified lines | ||
| 8 | 8 | import * as Schema from "effect/Schema"; |
| 9 | 9 | import { and, eq, isNull, ne, notExists } from "drizzle-orm"; |
| 10 | 10 | |
| 11 | −import { RelayDb } from "../db.ts"; | |
| 11 | +import * as RelayDb from "../db.ts"; | |
| 12 | 12 | import { relayEnvironmentCredentials, relayEnvironmentLinks } from "../persistence/schema.ts"; |
| 13 | 13 | |
| 14 | 14 | export class EnvironmentCredentialCreatePersistenceError extends Schema.TaggedErrorClass<EnvironmentCredentialCreatePersistenceError>()( |
| @@ -44,30 +44,28 @@ export interface EnvironmentCredentialPrincipal { · 29 unmodified lines | ||
| 44 | 44 | readonly environmentPublicKey: string; |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | −export interface EnvironmentCredentialsShape { | |
| 48 | − readonly create: (input: { | |
| 49 | − readonly environmentId: string; | |
| 50 | − readonly environmentPublicKey: string; | |
| 51 | − }) => Effect.Effect<string, EnvironmentCredentialCreatePersistenceError>; | |
| 52 | − readonly authenticate: ( | |
| 53 | − token: string, | |
| 54 | − ) => Effect.Effect< | |
| 55 | − Option.Option<EnvironmentCredentialPrincipal>, | |
| 56 | − EnvironmentCredentialAuthenticatePersistenceError | |
| 57 | − >; | |
| 58 | − readonly revokeForEnvironmentPublicKey: (input: { | |
| 59 | − readonly environmentId: string; | |
| 60 | − readonly environmentPublicKey: string; | |
| 61 | − }) => Effect.Effect<boolean, EnvironmentCredentialRevokePersistenceError>; | |
| 62 | −} | |
| 63 | − | |
| 64 | 47 | export class EnvironmentCredentials extends Context.Service< |
| 65 | 48 | EnvironmentCredentials, |
| 66 | − EnvironmentCredentialsShape | |
| 49 | + { | |
| 50 | + readonly create: (input: { | |
| 51 | + readonly environmentId: string; | |
| 52 | + readonly environmentPublicKey: string; | |
| 53 | + }) => Effect.Effect<string, EnvironmentCredentialCreatePersistenceError>; | |
| 54 | + readonly authenticate: ( | |
| 55 | + token: string, | |
| 56 | + ) => Effect.Effect< | |
| 57 | + Option.Option<EnvironmentCredentialPrincipal>, | |
| 58 | + EnvironmentCredentialAuthenticatePersistenceError | |
| 59 | + >; | |
| 60 | + readonly revokeForEnvironmentPublicKey: (input: { | |
| 61 | + readonly environmentId: string; | |
| 62 | + readonly environmentPublicKey: string; | |
| 63 | + }) => Effect.Effect<boolean, EnvironmentCredentialRevokePersistenceError>; | |
| 64 | + } | |
| 67 | 65 | >()("t3code-relay/environments/EnvironmentCredentials") {} |
| 68 | 66 | |
| 69 | 67 | const make = Effect.gen(function* () { |
| 70 | − const db = yield* RelayDb; | |
| 68 | + const db = yield* RelayDb.RelayDb; | |
| 71 | 69 | const crypto = yield* Crypto.Crypto; |
| 72 | 70 | const hashToken = (token: string) => |
| 73 | 71 | crypto |
| @@ -105,14 +105,14 @@ const makeRequest = Effect.gen(function* () { · 104 unmodified lines | ||
| 105 | 105 | }); |
| 106 | 106 | |
| 107 | 107 | function testLayer(input?: { |
| 108 | − readonly upsert?: EnvironmentLinks.EnvironmentLinksShape["upsert"]; | |
| 109 | − readonly consume?: DpopProofs.DpopProofReplayShape["consume"]; | |
| 108 | + readonly upsert?: EnvironmentLinks.EnvironmentLinks["Service"]["upsert"]; | |
| 109 | + readonly consume?: DpopProofs.DpopProofReplay["Service"]["consume"]; | |
| 110 | 110 | }) { |
| 111 | 111 | return EnvironmentLinker.layer.pipe( |
| 112 | 112 | Layer.provideMerge(RelayTokens.layer), |
| 113 | 113 | Layer.provide( |
| 114 | 114 | Layer.mergeAll( |
| 115 | − Layer.succeed(RelayConfiguration.RelayConfiguration, config), | |
| 115 | + RelayConfiguration.layer(config), | |
| 116 | 116 | Layer.succeed(DpopProofs.DpopProofReplay, { |
| 117 | 117 | verifyAndConsume: () => Effect.die("unexpected DPoP proof verification"), |
| 118 | 118 | consume: input?.consume ?? (() => Effect.succeed(true)), |
| @@ -53,26 +53,25 @@ export type EnvironmentLinkError = · 52 unmodified lines | ||
| 53 | 53 | | EnvironmentCredentials.EnvironmentCredentialCreatePersistenceError |
| 54 | 54 | | ManagedEndpointProvider.ManagedEndpointProviderError; |
| 55 | 55 | |
| 56 | −export interface EnvironmentLinkerShape { | |
| 57 | − readonly link: (input: { | |
| 58 | − readonly userId: string; | |
| 59 | − readonly request: RelayEnvironmentLinkRequest; | |
| 60 | − }) => Effect.Effect< | |
| 61 | − { | |
| 62 | − readonly environmentId: RelayEnvironmentLinkProofPayload["environmentId"]; | |
| 63 | − readonly endpoint: RelayEnvironmentLinkProofPayload["endpoint"]; | |
| 64 | − readonly endpointRuntime: | |
| 65 | − | ManagedEndpointProvider.ManagedEndpointProvisioningResult["runtime"] | |
| 66 | − | null; | |
| 67 | − readonly environmentCredential: string; | |
| 68 | − }, | |
| 69 | − EnvironmentLinkError | |
| 70 | − >; | |
| 71 | −} | |
| 72 | − | |
| 73 | −export class EnvironmentLinker extends Context.Service<EnvironmentLinker, EnvironmentLinkerShape>()( | |
| 74 | − "t3code-relay/environments/EnvironmentLinker", | |
| 75 | −) {} | |
| 56 | +export class EnvironmentLinker extends Context.Service< | |
| 57 | + EnvironmentLinker, | |
| 58 | + { | |
| 59 | + readonly link: (input: { | |
| 60 | + readonly userId: string; | |
| 61 | + readonly request: RelayEnvironmentLinkRequest; | |
| 62 | + }) => Effect.Effect< | |
| 63 | + { | |
| 64 | + readonly environmentId: RelayEnvironmentLinkProofPayload["environmentId"]; | |
| 65 | + readonly endpoint: RelayEnvironmentLinkProofPayload["endpoint"]; | |
| 66 | + readonly endpointRuntime: | |
| 67 | + | ManagedEndpointProvider.ManagedEndpointProvisioningResult["runtime"] | |
| 68 | + | null; | |
| 69 | + readonly environmentCredential: string; | |
| 70 | + }, | |
| 71 | + EnvironmentLinkError | |
| 72 | + >; | |
| 73 | + } | |
| 74 | +>()("t3code-relay/environments/EnvironmentLinker") {} | |
| 76 | 75 | |
| 77 | 76 | const decodeProof = Schema.decodeUnknownEffect(RelayEnvironmentLinkProofPayload); |
| 78 | 77 | |
| @@ -3,9 +3,9 @@ import * as Effect from "effect/Effect"; · 2 unmodified lines | ||
| 3 | 3 | import * as Layer from "effect/Layer"; |
| 4 | 4 | import { PgDialect } from "drizzle-orm/pg-core"; |
| 5 | 5 | |
| 6 | −import { RelayDb, type RelayDatabase } from "../db.ts"; | |
| 6 | +import * as RelayDb from "../db.ts"; | |
| 7 | 7 | import { relayEnvironmentLinks } from "../persistence/schema.ts"; |
| 8 | −import { EnvironmentLinks, layer } from "./EnvironmentLinks.ts"; | |
| 8 | +import * as EnvironmentLinks from "./EnvironmentLinks.ts"; | |
| 9 | 9 | |
| 10 | 10 | describe("EnvironmentLinks", () => { |
| 11 | 11 | it.effect("selects users when either notifications or Live Activities are enabled", () => { |
| @@ -25,10 +25,10 @@ describe("EnvironmentLinks", () => { · 13 unmodified lines | ||
| 25 | 25 | }, |
| 26 | 26 | }; |
| 27 | 27 | }, |
| 28 | − } as unknown as RelayDatabase; | |
| 28 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 29 | 29 | |
| 30 | 30 | return Effect.gen(function* () { |
| 31 | − const links = yield* EnvironmentLinks; | |
| 31 | + const links = yield* EnvironmentLinks.EnvironmentLinks; | |
| 32 | 32 | expect(yield* links.listUsersForEnvironment({ environmentId: "env-1" })).toEqual([]); |
| 33 | 33 | expect(whereConditions).toHaveLength(1); |
| 34 | 34 | |
| @@ -39,7 +39,11 @@ describe("EnvironmentLinks", () => { · 4 unmodified lines | ||
| 39 | 39 | expect(query.sql).toContain('"relay_environment_links"."live_activities_enabled" = $3'); |
| 40 | 40 | expect(query.sql).toContain(" or "); |
| 41 | 41 | expect(query.params).toEqual(["env-1", true, true]); |
| 42 | − }).pipe(Effect.provide(layer.pipe(Layer.provide(Layer.succeed(RelayDb, fakeDb))))); | |
| 42 | + }).pipe( | |
| 43 | + Effect.provide( | |
| 44 | + EnvironmentLinks.layer.pipe(Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb))), | |
| 45 | + ), | |
| 46 | + ); | |
| 43 | 47 | }); |
| 44 | 48 | |
| 45 | 49 | it.effect("revokes only the active link owned by the requesting user", () => { |
| @@ -65,10 +69,10 @@ describe("EnvironmentLinks", () => { · 19 unmodified lines | ||
| 65 | 69 | }, |
| 66 | 70 | }; |
| 67 | 71 | }, |
| 68 | − } as unknown as RelayDatabase; | |
| 72 | + } as unknown as RelayDb.RelayDb["Service"]; | |
| 69 | 73 | |
| 70 | 74 | return Effect.gen(function* () { |
| 71 | − const links = yield* EnvironmentLinks; | |
| 75 | + const links = yield* EnvironmentLinks.EnvironmentLinks; | |
| 72 | 76 | const revoked = yield* links.revokeForUser({ |
| 73 | 77 | userId: "user-1", |
| 74 | 78 | environmentId: "env-1", |
| @@ -86,6 +90,10 @@ describe("EnvironmentLinks", () => { · 11 unmodified lines | ||
| 86 | 90 | expect(query.sql).toContain('"relay_environment_links"."environment_id" = $2'); |
| 87 | 91 | expect(query.sql).toContain('"relay_environment_links"."revoked_at" is null'); |
| 88 | 92 | expect(query.params).toEqual(["user-1", "env-1"]); |
| 89 | − }).pipe(Effect.provide(layer.pipe(Layer.provide(Layer.succeed(RelayDb, fakeDb))))); | |
| 93 | + }).pipe( | |
| 94 | + Effect.provide( | |
| 95 | + EnvironmentLinks.layer.pipe(Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb))), | |
| 96 | + ), | |
| 97 | + ); | |
| 90 | 98 | }); |
| 91 | 99 | }); |
| @@ -11,7 +11,7 @@ import * as Layer from "effect/Layer"; · 10 unmodified lines | ||
| 11 | 11 | import * as Schema from "effect/Schema"; |
| 12 | 12 | import { and, eq, isNull, or } from "drizzle-orm"; |
| 13 | 13 | |
| 14 | −import { RelayDb } from "../db.ts"; | |
| 14 | +import * as RelayDb from "../db.ts"; | |
| 15 | 15 | import { relayEnvironmentLinks } from "../persistence/schema.ts"; |
| 16 | 16 | |
| 17 | 17 | export interface RelayLinkedEnvironmentRecord extends RelayClientEnvironmentRecord { |
| @@ -78,45 +78,44 @@ export class EnvironmentLinkRevokePersistenceError extends Schema.TaggedErrorCla · 60 unmodified lines | ||
| 78 | 78 | } |
| 79 | 79 | } |
| 80 | 80 | |
| 81 | −export interface EnvironmentLinksShape { | |
| 82 | − readonly upsert: (input: { | |
| 83 | − readonly userId: string; | |
| 84 | − readonly request: RelayEnvironmentLinkRequest; | |
| 85 | − readonly proof: RelayEnvironmentLinkProofPayload; | |
| 86 | − readonly endpoint: RelayManagedEndpoint; | |
| 87 | − }) => Effect.Effect<void, EnvironmentLinkUpsertPersistenceError>; | |
| 88 | − readonly listUsersForEnvironment: (input: { | |
| 89 | − readonly environmentId: string; | |
| 90 | − }) => Effect.Effect<ReadonlyArray<string>, EnvironmentLinkUserListPersistenceError>; | |
| 91 | − readonly listDeliveryUsersForEnvironment: (input: { | |
| 92 | − readonly environmentId: string; | |
| 93 | − readonly environmentPublicKey: string; | |
| 94 | − }) => Effect.Effect< | |
| 95 | − ReadonlyArray<AgentAwarenessDeliveryUserRecord>, | |
| 96 | − EnvironmentLinkUserListPersistenceError | |
| 97 | − >; | |
| 98 | − readonly listPublicKeysForEnvironment: (input: { | |
| 99 | − readonly environmentId: string; | |
| 100 | − }) => Effect.Effect<ReadonlyArray<string>, EnvironmentPublicKeyListPersistenceError>; | |
| 101 | − readonly listForUser: (input: { | |
| 102 | − readonly userId: string; | |
| 103 | − }) => Effect.Effect< | |
| 104 | − ReadonlyArray<RelayClientEnvironmentRecord>, | |
| 105 | − EnvironmentLinkListPersistenceError | |
| 106 | − >; | |
| 107 | − readonly getForUser: (input: { | |
| 108 | − readonly userId: string; | |
| 109 | − readonly environmentId: string; | |
| 110 | − }) => Effect.Effect<RelayLinkedEnvironmentRecord | null, EnvironmentLinkLookupPersistenceError>; | |
| 111 | − readonly revokeForUser: (input: { | |
| 112 | − readonly userId: string; | |
| 113 | − readonly environmentId: string; | |
| 114 | − }) => Effect.Effect<boolean, EnvironmentLinkRevokePersistenceError>; | |
| 115 | −} | |
| 116 | − | |
| 117 | −export class EnvironmentLinks extends Context.Service<EnvironmentLinks, EnvironmentLinksShape>()( | |
| 118 | − "t3code-relay/environments/EnvironmentLinks", | |
| 119 | −) {} | |
| 81 | +export class EnvironmentLinks extends Context.Service< | |
| 82 | + EnvironmentLinks, | |
| 83 | + { | |
| 84 | + readonly upsert: (input: { | |
| 85 | + readonly userId: string; | |
| 86 | + readonly request: RelayEnvironmentLinkRequest; | |
| 87 | + readonly proof: RelayEnvironmentLinkProofPayload; | |
| 88 | + readonly endpoint: RelayManagedEndpoint; | |
| 89 | + }) => Effect.Effect<void, EnvironmentLinkUpsertPersistenceError>; | |
| 90 | + readonly listUsersForEnvironment: (input: { | |
| 91 | + readonly environmentId: string; | |
| 92 | + }) => Effect.Effect<ReadonlyArray<string>, EnvironmentLinkUserListPersistenceError>; | |
| 93 | + readonly listDeliveryUsersForEnvironment: (input: { | |
| 94 | + readonly environmentId: string; | |
| 95 | + readonly environmentPublicKey: string; | |
| 96 | + }) => Effect.Effect< | |
| 97 | + ReadonlyArray<AgentAwarenessDeliveryUserRecord>, | |
| 98 | + EnvironmentLinkUserListPersistenceError | |
| 99 | + >; | |
| 100 | + readonly listPublicKeysForEnvironment: (input: { | |
| 101 | + readonly environmentId: string; | |
| 102 | + }) => Effect.Effect<ReadonlyArray<string>, EnvironmentPublicKeyListPersistenceError>; | |
| 103 | + readonly listForUser: (input: { | |
| 104 | + readonly userId: string; | |
| 105 | + }) => Effect.Effect< | |
| 106 | + ReadonlyArray<RelayClientEnvironmentRecord>, | |
| 107 | + EnvironmentLinkListPersistenceError | |
| 108 | + >; | |
| 109 | + readonly getForUser: (input: { | |
| 110 | + readonly userId: string; | |
| 111 | + readonly environmentId: string; | |
| 112 | + }) => Effect.Effect<RelayLinkedEnvironmentRecord | null, EnvironmentLinkLookupPersistenceError>; | |
| 113 | + readonly revokeForUser: (input: { | |
| 114 | + readonly userId: string; | |
| 115 | + readonly environmentId: string; | |
| 116 | + }) => Effect.Effect<boolean, EnvironmentLinkRevokePersistenceError>; | |
| 117 | + } | |
| 118 | +>()("t3code-relay/environments/EnvironmentLinks") {} | |
| 120 | 119 | |
| 121 | 120 | function agentAwarenessDeliveryUserCondition(environmentId: string) { |
| 122 | 121 | return and( |
| @@ -140,7 +139,7 @@ function agentAwarenessDeliveryUserKeyCondition(input: { · 17 unmodified lines | ||
| 140 | 139 | } |
| 141 | 140 | |
| 142 | 141 | const make = Effect.gen(function* () { |
| 143 | − const db = yield* RelayDb; | |
| 142 | + const db = yield* RelayDb.RelayDb; | |
| 144 | 143 | |
| 145 | 144 | return EnvironmentLinks.of({ |
| 146 | 145 | upsert: Effect.fn("relay.environment_links.upsert")( |
| @@ -80,11 +80,11 @@ const freshRequest = Effect.gen(function* () { · 79 unmodified lines | ||
| 80 | 80 | } satisfies RelayAgentActivityPublishRequest; |
| 81 | 81 | }); |
| 82 | 82 | |
| 83 | −function layer(replay?: Partial<DpopProofs.DpopProofReplayShape>) { | |
| 83 | +function layer(replay?: Partial<DpopProofs.DpopProofReplay["Service"]>) { | |
| 84 | 84 | return EnvironmentPublishSignatures.layer.pipe( |
| 85 | 85 | Layer.provide( |
| 86 | 86 | Layer.merge( |
| 87 | − Layer.succeed(RelayConfiguration.RelayConfiguration, config), | |
| 87 | + RelayConfiguration.layer(config), | |
| 88 | 88 | Layer.succeed(DpopProofs.DpopProofReplay, { |
| 89 | 89 | verifyAndConsume: |
| 90 | 90 | replay?.verifyAndConsume ?? (() => Effect.die("unexpected DPoP proof verification")), |
| @@ -59,18 +59,16 @@ export type EnvironmentPublishSignatureError = · 58 unmodified lines | ||
| 59 | 59 | | EnvironmentPublishPublicKeyMissing |
| 60 | 60 | | DpopProofs.DpopProofReplayPersistenceError; |
| 61 | 61 | |
| 62 | −export interface EnvironmentPublishSignaturesShape { | |
| 63 | − readonly verify: (input: { | |
| 64 | − readonly environmentId: string; | |
| 65 | − readonly environmentPublicKey: string; | |
| 66 | − readonly threadId: string; | |
| 67 | − readonly request: RelayAgentActivityPublishRequest; | |
| 68 | − }) => Effect.Effect<void, EnvironmentPublishSignatureError>; | |
| 69 | −} | |
| 70 | − | |
| 71 | 62 | export class EnvironmentPublishSignatures extends Context.Service< |
| 72 | 63 | EnvironmentPublishSignatures, |
| 73 | − EnvironmentPublishSignaturesShape | |
| 64 | + { | |
| 65 | + readonly verify: (input: { | |
| 66 | + readonly environmentId: string; | |
| 67 | + readonly environmentPublicKey: string; | |
| 68 | + readonly threadId: string; | |
| 69 | + readonly request: RelayAgentActivityPublishRequest; | |
| 70 | + }) => Effect.Effect<void, EnvironmentPublishSignatureError>; | |
| 71 | + } | |
| 74 | 72 | >()("t3code-relay/environments/EnvironmentPublishSignatures") {} |
| 75 | 73 | |
| 76 | 74 | const decodeProof = Schema.decodeUnknownEffect(RelayAgentActivityPublishProofPayload); |
| @@ -6,7 +6,7 @@ import * as Effect from "effect/Effect"; · 5 unmodified lines | ||
| 6 | 6 | import * as Layer from "effect/Layer"; |
| 7 | 7 | import * as Schema from "effect/Schema"; |
| 8 | 8 | |
| 9 | −import { RelayDb } from "../db.ts"; | |
| 9 | +import * as RelayDb from "../db.ts"; | |
| 10 | 10 | import { isManagedEndpointHostname, managedEndpointForHostname } from "../deploymentConfig.ts"; |
| 11 | 11 | import { relayManagedEndpointAllocations } from "../persistence/schema.ts"; |
| 12 | 12 | |
| @@ -66,26 +66,29 @@ interface RecordManagedEndpointDnsInput extends ManagedEndpointAllocationKey { · 53 unmodified lines | ||
| 66 | 66 | readonly dnsRecordId: string; |
| 67 | 67 | } |
| 68 | 68 | |
| 69 | −export interface ManagedEndpointAllocationsShape { | |
| 70 | − readonly get: ( | |
| 71 | − input: ManagedEndpointAllocationKey, | |
| 72 | − ) => Effect.Effect<ManagedEndpointAllocation | null, ManagedEndpointAllocationPersistenceError>; | |
| 73 | − readonly reserve: ( | |
| 74 | − input: ReserveManagedEndpointAllocationInput, | |
| 75 | − ) => Effect.Effect<ManagedEndpointAllocation, ManagedEndpointAllocationPersistenceError>; | |
| 76 | − readonly recordTunnel: ( | |
| 77 | − input: RecordManagedEndpointTunnelInput, | |
| 78 | − ) => Effect.Effect<void, ManagedEndpointAllocationPersistenceError>; | |
| 79 | − readonly recordDns: ( | |
| 80 | − input: RecordManagedEndpointDnsInput, | |
| 81 | − ) => Effect.Effect<void, ManagedEndpointAllocationPersistenceError>; | |
| 82 | − readonly markReady: ( | |
| 83 | − input: ManagedEndpointAllocationKey, | |
| 84 | − ) => Effect.Effect<void, ManagedEndpointAllocationPersistenceError>; | |
| 85 | − readonly remove: ( | |
| 86 | − input: ManagedEndpointAllocationKey, | |
| 87 | − ) => Effect.Effect<void, ManagedEndpointAllocationPersistenceError>; | |
| 88 | −} | |
| 69 | +export class ManagedEndpointAllocations extends Context.Service< | |
| 70 | + ManagedEndpointAllocations, | |
| 71 | + { | |
| 72 | + readonly get: ( | |
| 73 | + input: ManagedEndpointAllocationKey, | |
| 74 | + ) => Effect.Effect<ManagedEndpointAllocation | null, ManagedEndpointAllocationPersistenceError>; | |
| 75 | + readonly reserve: ( | |
| 76 | + input: ReserveManagedEndpointAllocationInput, | |
| 77 | + ) => Effect.Effect<ManagedEndpointAllocation, ManagedEndpointAllocationPersistenceError>; | |
| 78 | + readonly recordTunnel: ( | |
| 79 | + input: RecordManagedEndpointTunnelInput, | |
| 80 | + ) => Effect.Effect<void, ManagedEndpointAllocationPersistenceError>; | |
| 81 | + readonly recordDns: ( | |
| 82 | + input: RecordManagedEndpointDnsInput, | |
| 83 | + ) => Effect.Effect<void, ManagedEndpointAllocationPersistenceError>; | |
| 84 | + readonly markReady: ( | |
| 85 | + input: ManagedEndpointAllocationKey, | |
| 86 | + ) => Effect.Effect<void, ManagedEndpointAllocationPersistenceError>; | |
| 87 | + readonly remove: ( | |
| 88 | + input: ManagedEndpointAllocationKey, | |
| 89 | + ) => Effect.Effect<void, ManagedEndpointAllocationPersistenceError>; | |
| 90 | + } | |
| 91 | +>()("t3code-relay/environments/ManagedEndpointAllocations") {} | |
| 89 | 92 | |
| 90 | 93 | const allocationSelection = { |
| 91 | 94 | userId: relayManagedEndpointAllocations.userId, |
| @@ -109,7 +112,7 @@ const persistenceError = (cause: unknown) => · 17 unmodified lines | ||
| 109 | 112 | : new ManagedEndpointAllocationPersistenceError({ cause }); |
| 110 | 113 | |
| 111 | 114 | const make = Effect.gen(function* () { |
| 112 | − const db = yield* RelayDb; | |
| 115 | + const db = yield* RelayDb.RelayDb; | |
| 113 | 116 | |
| 114 | 117 | return ManagedEndpointAllocations.of({ |
| 115 | 118 | get: Effect.fn("relay.managed_endpoint_allocations.get")(function* ( |
| @@ -198,9 +201,4 @@ const make = Effect.gen(function* () { · 82 unmodified lines | ||
| 198 | 201 | }); |
| 199 | 202 | }); |
| 200 | 203 | |
| 201 | −export class ManagedEndpointAllocations extends Context.Service< | |
| 202 | − ManagedEndpointAllocations, | |
| 203 | − ManagedEndpointAllocationsShape | |
| 204 | −>()("t3code-relay/environments/ManagedEndpointAllocations") { | |
| 205 | − static readonly layer = Layer.effect(this, make); | |
| 206 | −} | |
| 204 | +export const layer = Layer.effect(ManagedEndpointAllocations, make); | |
| @@ -204,9 +204,9 @@ function providerLayer( · 203 unmodified lines | ||
| 204 | 204 | ) { |
| 205 | 205 | return ManagedEndpointProvider.layer.pipe( |
| 206 | 206 | Layer.provideMerge(NodeServices.layer), |
| 207 | − Layer.provide(Layer.succeed(RelayConfiguration.RelayConfiguration, config)), | |
| 208 | − Layer.provide(Layer.succeed(ManagedEndpointProvider.ManagedEndpointTunnelClient, tunnelClient)), | |
| 209 | − Layer.provide(Layer.succeed(ManagedEndpointProvider.ManagedEndpointDnsClient, dnsClient)), | |
| 207 | + Layer.provide(RelayConfiguration.layer(config)), | |
| 208 | + Layer.provide(ManagedEndpointProvider.layerTunnelClient(tunnelClient)), | |
| 209 | + Layer.provide(ManagedEndpointProvider.layerDnsClient(dnsClient)), | |
| 210 | 210 | Layer.provide( |
| 211 | 211 | Layer.succeed(ManagedEndpointAllocations.ManagedEndpointAllocations, allocations), |
| 212 | 212 | ), |
| @@ -23,7 +23,7 @@ import { · 22 unmodified lines | ||
| 23 | 23 | managedEndpointHostname, |
| 24 | 24 | managedEndpointTunnelName, |
| 25 | 25 | } from "../deploymentConfig.ts"; |
| 26 | −import { ManagedEndpointAllocations } from "./ManagedEndpointAllocations.ts"; | |
| 26 | +import * as ManagedEndpointAllocations from "./ManagedEndpointAllocations.ts"; | |
| 27 | 27 | |
| 28 | 28 | export class ManagedEndpointProvisioningNotConfigured extends Schema.TaggedErrorClass<ManagedEndpointProvisioningNotConfigured>()( |
| 29 | 29 | "ManagedEndpointProvisioningNotConfigured", |
| @@ -74,21 +74,19 @@ export interface ManagedEndpointProvisioningResult { · 44 unmodified lines | ||
| 74 | 74 | readonly runtime: RelayManagedEndpointRuntimeConfig; |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | −export interface ManagedEndpointProviderShape { | |
| 78 | − readonly provision: (input: { | |
| 79 | − readonly userId: string; | |
| 80 | − readonly environmentId: string; | |
| 81 | − readonly origin: RelayManagedEndpointOrigin; | |
| 82 | − }) => Effect.Effect<ManagedEndpointProvisioningResult, ManagedEndpointProviderError>; | |
| 83 | − readonly deprovision: (input: { | |
| 84 | − readonly userId: string; | |
| 85 | − readonly environmentId: string; | |
| 86 | − }) => Effect.Effect<void, ManagedEndpointDeprovisioningFailed>; | |
| 87 | −} | |
| 88 | − | |
| 89 | 77 | export class ManagedEndpointProvider extends Context.Service< |
| 90 | 78 | ManagedEndpointProvider, |
| 91 | − ManagedEndpointProviderShape | |
| 79 | + { | |
| 80 | + readonly provision: (input: { | |
| 81 | + readonly userId: string; | |
| 82 | + readonly environmentId: string; | |
| 83 | + readonly origin: RelayManagedEndpointOrigin; | |
| 84 | + }) => Effect.Effect<ManagedEndpointProvisioningResult, ManagedEndpointProviderError>; | |
| 85 | + readonly deprovision: (input: { | |
| 86 | + readonly userId: string; | |
| 87 | + readonly environmentId: string; | |
| 88 | + }) => Effect.Effect<void, ManagedEndpointDeprovisioningFailed>; | |
| 89 | + } | |
| 92 | 90 | >()("t3code-relay/environments/ManagedEndpointProvider") {} |
| 93 | 91 | |
| 94 | 92 | interface ManagedEndpointTunnel { |
| @@ -105,36 +103,42 @@ export class ManagedEndpointTunnelClientError extends Schema.TaggedErrorClass<Ma · 10 unmodified lines | ||
| 105 | 103 | } |
| 106 | 104 | } |
| 107 | 105 | |
| 108 | −export interface ManagedEndpointTunnelClientShape { | |
| 109 | − readonly list: (request: { | |
| 110 | − readonly name: string; | |
| 111 | − readonly isDeleted: false; | |
| 112 | − }) => Effect.Effect< | |
| 113 | − { readonly result: ReadonlyArray<ManagedEndpointTunnel> }, | |
| 114 | − ManagedEndpointTunnelClientError | |
| 115 | − >; | |
| 116 | − readonly create: (request: { | |
| 117 | − readonly name: string; | |
| 118 | − readonly configSrc: "cloudflare"; | |
| 119 | − }) => Effect.Effect<ManagedEndpointTunnel, ManagedEndpointTunnelClientError>; | |
| 120 | − readonly putConfiguration: ( | |
| 121 | − tunnelId: string, | |
| 122 | − config: { | |
| 123 | − readonly ingress: Array<{ | |
| 124 | − readonly hostname?: string; | |
| 125 | − readonly service: string; | |
| 126 | − }>; | |
| 127 | − }, | |
| 128 | − ) => Effect.Effect<unknown, ManagedEndpointTunnelClientError>; | |
| 129 | − readonly getToken: (tunnelId: string) => Effect.Effect<string, ManagedEndpointTunnelClientError>; | |
| 130 | − readonly delete: (tunnelId: string) => Effect.Effect<unknown, ManagedEndpointTunnelClientError>; | |
| 131 | −} | |
| 132 | − | |
| 133 | 106 | export class ManagedEndpointTunnelClient extends Context.Service< |
| 134 | 107 | ManagedEndpointTunnelClient, |
| 135 | − ManagedEndpointTunnelClientShape | |
| 108 | + { | |
| 109 | + readonly list: (request: { | |
| 110 | + readonly name: string; | |
| 111 | + readonly isDeleted: false; | |
| 112 | + }) => Effect.Effect< | |
| 113 | + { readonly result: ReadonlyArray<ManagedEndpointTunnel> }, | |
| 114 | + ManagedEndpointTunnelClientError | |
| 115 | + >; | |
| 116 | + readonly create: (request: { | |
| 117 | + readonly name: string; | |
| 118 | + readonly configSrc: "cloudflare"; | |
| 119 | + }) => Effect.Effect<ManagedEndpointTunnel, ManagedEndpointTunnelClientError>; | |
| 120 | + readonly putConfiguration: ( | |
| 121 | + tunnelId: string, | |
| 122 | + config: { | |
| 123 | + readonly ingress: Array<{ | |
| 124 | + readonly hostname?: string; | |
| 125 | + readonly service: string; | |
| 126 | + }>; | |
| 127 | + }, | |
| 128 | + ) => Effect.Effect<unknown, ManagedEndpointTunnelClientError>; | |
| 129 | + readonly getToken: ( | |
| 130 | + tunnelId: string, | |
| 131 | + ) => Effect.Effect<string, ManagedEndpointTunnelClientError>; | |
| 132 | + readonly delete: (tunnelId: string) => Effect.Effect<unknown, ManagedEndpointTunnelClientError>; | |
| 133 | + } | |
| 136 | 134 | >()("t3code-relay/environments/ManagedEndpointProvider/ManagedEndpointTunnelClient") {} |
| 137 | 135 | |
| 136 | +export const makeTunnelClient = (client: ManagedEndpointTunnelClient["Service"]) => | |
| 137 | + ManagedEndpointTunnelClient.of(client); | |
| 138 | + | |
| 139 | +export const layerTunnelClient = (client: ManagedEndpointTunnelClient["Service"]) => | |
| 140 | + Layer.succeed(ManagedEndpointTunnelClient, makeTunnelClient(client)); | |
| 141 | + | |
| 138 | 142 | interface ManagedEndpointCnameRecordInput { |
| 139 | 143 | readonly type: "CNAME"; |
| 140 | 144 | readonly name: string; |
| @@ -152,29 +156,33 @@ export class ManagedEndpointDnsClientError extends Schema.TaggedErrorClass<Manag · 11 unmodified lines | ||
| 152 | 156 | } |
| 153 | 157 | } |
| 154 | 158 | |
| 155 | −export interface ManagedEndpointDnsClientShape { | |
| 156 | − readonly listRecords: ( | |
| 157 | − hostname: string, | |
| 158 | − ) => Effect.Effect<ReadonlyArray<{ readonly id: string }>, ManagedEndpointDnsClientError>; | |
| 159 | − readonly createRecord: ( | |
| 160 | − request: ManagedEndpointCnameRecordInput, | |
| 161 | − ) => Effect.Effect<{ readonly id: string }, ManagedEndpointDnsClientError>; | |
| 162 | − readonly updateRecord: ( | |
| 163 | − dnsRecordId: string, | |
| 164 | − request: ManagedEndpointCnameRecordInput, | |
| 165 | − ) => Effect.Effect<unknown, ManagedEndpointDnsClientError>; | |
| 166 | − readonly deleteRecord: ( | |
| 167 | − dnsRecordId: string, | |
| 168 | − ) => Effect.Effect<unknown, ManagedEndpointDnsClientError>; | |
| 169 | −} | |
| 170 | − | |
| 171 | 159 | export class ManagedEndpointDnsClient extends Context.Service< |
| 172 | 160 | ManagedEndpointDnsClient, |
| 173 | − ManagedEndpointDnsClientShape | |
| 161 | + { | |
| 162 | + readonly listRecords: ( | |
| 163 | + hostname: string, | |
| 164 | + ) => Effect.Effect<ReadonlyArray<{ readonly id: string }>, ManagedEndpointDnsClientError>; | |
| 165 | + readonly createRecord: ( | |
| 166 | + request: ManagedEndpointCnameRecordInput, | |
| 167 | + ) => Effect.Effect<{ readonly id: string }, ManagedEndpointDnsClientError>; | |
| 168 | + readonly updateRecord: ( | |
| 169 | + dnsRecordId: string, | |
| 170 | + request: ManagedEndpointCnameRecordInput, | |
| 171 | + ) => Effect.Effect<unknown, ManagedEndpointDnsClientError>; | |
| 172 | + readonly deleteRecord: ( | |
| 173 | + dnsRecordId: string, | |
| 174 | + ) => Effect.Effect<unknown, ManagedEndpointDnsClientError>; | |
| 175 | + } | |
| 174 | 176 | >()("t3code-relay/environments/ManagedEndpointProvider/ManagedEndpointDnsClient") {} |
| 175 | 177 | |
| 178 | +export const makeDnsClient = (client: ManagedEndpointDnsClient["Service"]) => | |
| 179 | + ManagedEndpointDnsClient.of(client); | |
| 180 | + | |
| 181 | +export const layerDnsClient = (client: ManagedEndpointDnsClient["Service"]) => | |
| 182 | + Layer.succeed(ManagedEndpointDnsClient, makeDnsClient(client)); | |
| 183 | + | |
| 176 | 184 | const requireCloudflareSettings = Effect.fnUntraced(function* ( |
| 177 | − settings: RelayConfiguration.RelayConfigurationShape, | |
| 185 | + settings: RelayConfiguration.RelayConfiguration["Service"], | |
| 178 | 186 | ) { |
| 179 | 187 | if (!settings.managedEndpointBaseDomain || !settings.managedEndpointNamespace) { |
| 180 | 188 | return yield* new ManagedEndpointProvisioningNotConfigured(); |
| @@ -234,7 +242,7 @@ const make = Effect.gen(function* () { · 53 unmodified lines | ||
| 234 | 242 | const crypto = yield* Crypto.Crypto; |
| 235 | 243 | const tunnels = yield* ManagedEndpointTunnelClient; |
| 236 | 244 | const dns = yield* ManagedEndpointDnsClient; |
| 237 | − const allocations = yield* ManagedEndpointAllocations; | |
| 245 | + const allocations = yield* ManagedEndpointAllocations.ManagedEndpointAllocations; | |
| 238 | 246 | |
| 239 | 247 | const updateExistingDnsRecords = Effect.fnUntraced(function* ( |
| 240 | 248 | records: ReadonlyArray<{ readonly id: string }>, |
| @@ -452,8 +460,7 @@ export const layerCloudflareBindings = ( · 211 unmodified lines | ||
| 452 | 460 | layer.pipe( |
| 453 | 461 | Layer.provide( |
| 454 | 462 | Layer.mergeAll( |
| 455 | − Layer.succeed( | |
| 456 | − ManagedEndpointTunnelClient, | |
| 463 | + layerTunnelClient( | |
| 457 | 464 | ManagedEndpointTunnelClient.of({ |
| 458 | 465 | list: (request) => |
| 459 | 466 | tunnelClient.list(request).pipe( |
| @@ -482,8 +489,7 @@ export const layerCloudflareBindings = ( · 22 unmodified lines | ||
| 482 | 489 | ), |
| 483 | 490 | }), |
| 484 | 491 | ), |
| 485 | − Layer.succeed( | |
| 486 | − ManagedEndpointDnsClient, | |
| 492 | + layerDnsClient( | |
| 487 | 493 | ManagedEndpointDnsClient.of({ |
| 488 | 494 | listRecords: (hostname) => |
| 489 | 495 | dnsClient.listDnsRecords({ search: hostname }).pipe( |
| @@ -30,7 +30,7 @@ vi.mock("@clerk/backend", () => ({ · 29 unmodified lines | ||
| 30 | 30 | verifyToken: vi.fn(), |
| 31 | 31 | })); |
| 32 | 32 | |
| 33 | −const relaySettings: RelayConfiguration.RelayConfigurationShape = { | |
| 33 | +const relaySettings: RelayConfiguration.RelayConfiguration["Service"] = { | |
| 34 | 34 | relayIssuer: "https://relay.example.test", |
| 35 | 35 | apns: { |
| 36 | 36 | teamId: "apns-team", |
| @@ -110,7 +110,7 @@ describe("relay environment authentication", () => { · 73 unmodified lines | ||
| 110 | 110 | const failure = new EnvironmentCredentials.EnvironmentCredentialAuthenticatePersistenceError({ |
| 111 | 111 | cause: "database unavailable", |
| 112 | 112 | }); |
| 113 | − const credentials: EnvironmentCredentials.EnvironmentCredentialsShape = { | |
| 113 | + const credentials: EnvironmentCredentials.EnvironmentCredentials["Service"] = { | |
| 114 | 114 | create: () => Effect.die("unused create"), |
| 115 | 115 | authenticate: () => Effect.fail(failure), |
| 116 | 116 | revokeForEnvironmentPublicKey: () => Effect.die("unused revoke"), |
| @@ -66,7 +66,7 @@ import * as ManagedEndpointAllocations from "../environments/ManagedEndpointAllo · 65 unmodified lines | ||
| 66 | 66 | import * as EnvironmentPublishSignatures from "../environments/EnvironmentPublishSignatures.ts"; |
| 67 | 67 | import * as MobileRegistrations from "../agentActivity/MobileRegistrations.ts"; |
| 68 | 68 | import { withSpanAttributes } from "../observability.ts"; |
| 69 | −import { RelayDb } from "../db.ts"; | |
| 69 | +import * as RelayDb from "../db.ts"; | |
| 70 | 70 | |
| 71 | 71 | const relayCorsAllowedMethods = ["GET", "POST", "DELETE", "OPTIONS"] as const; |
| 72 | 72 | const relayCorsAllowedHeaders = [ |
| @@ -347,7 +347,7 @@ export const healthApi = HttpApiBuilder.group( · 274 unmodified lines | ||
| 347 | 347 | RelayApi, |
| 348 | 348 | "health", |
| 349 | 349 | Effect.fnUntraced(function* (handlers) { |
| 350 | − const db = yield* RelayDb; | |
| 350 | + const db = yield* RelayDb.RelayDb; | |
| 351 | 351 | return handlers.handle( |
| 352 | 352 | "health", |
| 353 | 353 | Effect.fn("relay.api.health")( |
| @@ -985,7 +985,10 @@ function hasExpectedClerkAudience(audience: unknown, expectedAudience: string): · 631 unmodified lines | ||
| 985 | 985 | audience.some((entry) => typeof entry === "string" && entry === expectedAudience); |
| 986 | 986 | } |
| 987 | 987 | |
| 988 | −function verifyClerkBearerToken(config: RelayConfiguration.RelayConfigurationShape, token: string) { | |
| 988 | +function verifyClerkBearerToken( | |
| 989 | + config: RelayConfiguration.RelayConfiguration["Service"], | |
| 990 | + token: string, | |
| 991 | +) { | |
| 989 | 992 | return Effect.tryPromise({ |
| 990 | 993 | try: () => |
| 991 | 994 | verifyToken(token, { |
| @@ -1001,7 +1004,7 @@ function verifyClerkBearerToken(config: RelayConfiguration.RelayConfigurationSha · 9 unmodified lines | ||
| 1001 | 1004 | } |
| 1002 | 1005 | |
| 1003 | 1006 | function verifyClerkOAuthBearerToken( |
| 1004 | − config: RelayConfiguration.RelayConfigurationShape, | |
| 1007 | + config: RelayConfiguration.RelayConfiguration["Service"], | |
| 1005 | 1008 | token: string, |
| 1006 | 1009 | ) { |
| 1007 | 1010 | return Effect.tryPromise({ |
| @@ -1027,7 +1030,7 @@ function verifyClerkOAuthBearerToken( · 19 unmodified lines | ||
| 1027 | 1030 | } |
| 1028 | 1031 | |
| 1029 | 1032 | export function verifyRelayClientBearerToken( |
| 1030 | − config: RelayConfiguration.RelayConfigurationShape, | |
| 1033 | + config: RelayConfiguration.RelayConfiguration["Service"], | |
| 1031 | 1034 | token: string, |
| 1032 | 1035 | ) { |
| 1033 | 1036 | return verifyClerkBearerToken(config, token).pipe( |
| @@ -9,7 +9,7 @@ import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest"; · 8 unmodified lines | ||
| 9 | 9 | import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; |
| 10 | 10 | import type { OtlpTracer } from "effect/unstable/observability"; |
| 11 | 11 | |
| 12 | −import { EnvironmentConnectNotAuthorized } from "./environments/EnvironmentConnector.ts"; | |
| 12 | +import * as EnvironmentConnector from "./environments/EnvironmentConnector.ts"; | |
| 13 | 13 | import { makeRelayTraceLayer } from "./observability.ts"; |
| 14 | 14 | |
| 15 | 15 | interface ExportedRequest { |
| @@ -43,7 +43,7 @@ it.effect("exports schema error fields as span attributes", () => · 27 unmodified lines | ||
| 43 | 43 | ); |
| 44 | 44 | |
| 45 | 45 | yield* Effect.fail( |
| 46 | − new EnvironmentConnectNotAuthorized({ | |
| 46 | + new EnvironmentConnector.EnvironmentConnectNotAuthorized({ | |
| 47 | 47 | environmentId: "environment-1", |
| 48 | 48 | operation: "connect", |
| 49 | 49 | reason: "managed_endpoint_allocation_not_ready", |
| @@ -42,7 +42,7 @@ import * as EnvironmentCredentials from "./environments/EnvironmentCredentials.t · 41 unmodified lines | ||
| 42 | 42 | import * as EnvironmentLinks from "./environments/EnvironmentLinks.ts"; |
| 43 | 43 | import * as ManagedEndpointAllocations from "./environments/ManagedEndpointAllocations.ts"; |
| 44 | 44 | import * as LiveActivities from "./agentActivity/LiveActivities.ts"; |
| 45 | −import { RelayDb, RelayHyperdrive } from "./db.ts"; | |
| 45 | +import * as RelayDb from "./db.ts"; | |
| 46 | 46 | import { RelayApnsDeliveryDeadLetterQueue, RelayApnsDeliveryQueue } from "./queues.ts"; |
| 47 | 47 | import * as RelayConfiguration from "./Config.ts"; |
| 48 | 48 | import * as AgentActivityPublisher from "./agentActivity/AgentActivityPublisher.ts"; |
| @@ -138,7 +138,7 @@ export default class Api extends Cloudflare.Worker<Api>()( · 89 unmodified lines | ||
| 138 | 138 | |
| 139 | 139 | const cloudMintPrivateKey = yield* cloudMintKeyPair.privateKey; |
| 140 | 140 | const cloudMintPublicKey = yield* cloudMintKeyPair.publicKey; |
| 141 | − const hyperdrive = yield* Cloudflare.Hyperdrive.bind(yield* RelayHyperdrive); | |
| 141 | + const hyperdrive = yield* Cloudflare.Hyperdrive.bind(yield* RelayDb.RelayHyperdrive); | |
| 142 | 142 | const db = yield* Drizzle.postgres(hyperdrive.connectionString); |
| 143 | 143 | |
| 144 | 144 | const managedEndpointTunnelBinding = yield* Cloudflare.TunnelReadWrite.bind(); |
| @@ -203,16 +203,11 @@ export default class Api extends Cloudflare.Worker<Api>()( · 58 unmodified lines | ||
| 203 | 203 | Layer.provideMerge(AgentActivityRows.layer), |
| 204 | 204 | Layer.provideMerge(Devices.layer), |
| 205 | 205 | Layer.provideMerge(EnvironmentCredentials.layer), |
| 206 | − Layer.provideMerge( | |
| 207 | − Layer.mergeAll( | |
| 208 | − EnvironmentLinks.layer, | |
| 209 | − ManagedEndpointAllocations.ManagedEndpointAllocations.layer, | |
| 210 | − ), | |
| 211 | − ), | |
| 206 | + Layer.provideMerge(Layer.mergeAll(EnvironmentLinks.layer, ManagedEndpointAllocations.layer)), | |
| 212 | 207 | Layer.provideMerge(LiveActivities.layer), |
| 213 | 208 | Layer.provideMerge(DeliveryAttempts.layer), |
| 214 | 209 | Layer.provideMerge(RelayTokens.layer), |
| 215 | − Layer.provideMerge(Layer.succeed(RelayDb, db)), | |
| 210 | + Layer.provideMerge(Layer.succeed(RelayDb.RelayDb, db)), | |
| 216 | 211 | Layer.provideMerge(Layer.effect(RelayConfiguration.RelayConfiguration, loadSettings)), |
| 217 | 212 | Layer.provideMerge(webcryptoLayer), |
| 218 | 213 | ); |
| @@ -37,6 +37,10 @@ export function createPreviewEnvironmentAtoms<R, E>( · 36 unmodified lines | ||
| 37 | 37 | automationRequests: createEnvironmentRpcSubscriptionAtomFamily(runtime, { |
| 38 | 38 | label: "environment-data:preview:automation-requests", |
| 39 | 39 | tag: WS_METHODS.previewAutomationConnect, |
| 40 | + // Automation requests are commands, not cached query data. Dispose the | |
| 41 | + // stream immediately with its owner so stale requests cannot replay when | |
| 42 | + // a thread remounts and the server can clear disconnected hosts promptly. | |
| 43 | + idleTtlMs: 0, | |
| 40 | 44 | }), |
| 41 | 45 | open: createEnvironmentRpcCommand(runtime, { |
| 42 | 46 | label: "environment-data:preview:open", |
| @@ -125,6 +125,8 @@ export const VcsListRefsInput = Schema.Struct({ · 124 unmodified lines | ||
| 125 | 125 | cwd: TrimmedNonEmptyStringSchema, |
| 126 | 126 | query: Schema.optional(TrimmedNonEmptyStringSchema.check(Schema.isMaxLength(256))), |
| 127 | 127 | cursor: Schema.optional(NonNegativeInt), |
| 128 | + includeMatchingRemoteRefs: Schema.optional(Schema.Boolean), | |
| 129 | + refKind: Schema.optional(Schema.Literals(["all", "local", "remote"])), | |
| 128 | 130 | limit: Schema.optional( |
| 129 | 131 | PositiveInt.check(Schema.isLessThanOrEqualTo(GIT_LIST_BRANCHES_MAX_LIMIT)), |
| 130 | 132 | ), |
| @@ -75,6 +75,7 @@ import { · 74 unmodified lines | ||
| 75 | 75 | PreviewAutomationClickInput, |
| 76 | 76 | PreviewAutomationEvaluateInput, |
| 77 | 77 | PreviewAutomationOwner, |
| 78 | + PreviewAutomationOwnerIdentity, | |
| 78 | 79 | PreviewAutomationPressInput, |
| 79 | 80 | PreviewAutomationRequest, |
| 80 | 81 | PreviewAutomationResponse, |
| @@ -1232,7 +1233,7 @@ export interface EnvironmentApi { · 1151 unmodified lines | ||
| 1232 | 1233 | ) => () => void; |
| 1233 | 1234 | respond: (response: PreviewAutomationResponse) => Promise<void>; |
| 1234 | 1235 | reportOwner: (owner: PreviewAutomationOwner) => Promise<void>; |
| 1235 | − clearOwner: (input: { clientId: string }) => Promise<void>; | |
| 1236 | + clearOwner: (input: PreviewAutomationOwnerIdentity) => Promise<void>; | |
| 1236 | 1237 | }; |
| 1237 | 1238 | onEvent: ( |
| 1238 | 1239 | callback: (event: PreviewEvent) => void, |
| @@ -410,10 +410,15 @@ export const PreviewAutomationRecordingArtifact = Schema.Struct({ · 409 unmodified lines | ||
| 410 | 410 | }); |
| 411 | 411 | export type PreviewAutomationRecordingArtifact = typeof PreviewAutomationRecordingArtifact.Type; |
| 412 | 412 | |
| 413 | −export const PreviewAutomationOwner = Schema.Struct({ | |
| 413 | +export const PreviewAutomationOwnerIdentity = Schema.Struct({ | |
| 414 | 414 | clientId: TrimmedNonEmptyString, |
| 415 | 415 | environmentId: EnvironmentId, |
| 416 | 416 | threadId: ThreadId, |
| 417 | +}); | |
| 418 | +export type PreviewAutomationOwnerIdentity = typeof PreviewAutomationOwnerIdentity.Type; | |
| 419 | + | |
| 420 | +export const PreviewAutomationOwner = Schema.Struct({ | |
| 421 | + ...PreviewAutomationOwnerIdentity.fields, | |
| 417 | 422 | tabId: Schema.NullOr(PreviewTabId), |
| 418 | 423 | visible: Schema.Boolean, |
| 419 | 424 | supportsAutomation: Schema.Boolean, |
| @@ -1,5 +1,5 @@ | ||
| 1 | −import * as Schema from "effect/Schema"; | |
| 2 | 1 | import * as Context from "effect/Context"; |
| 2 | +import * as Schema from "effect/Schema"; | |
| 3 | 3 | import * as HttpApi from "effect/unstable/httpapi/HttpApi"; |
| 4 | 4 | import * as HttpApiEndpoint from "effect/unstable/httpapi/HttpApiEndpoint"; |
| 5 | 5 | import * as HttpApiGroup from "effect/unstable/httpapi/HttpApiGroup"; |
| @@ -512,26 +512,22 @@ const RelayAgentActivityPublishErrors = [ · 506 unmodified lines | ||
| 512 | 512 | RelayInternalError, |
| 513 | 513 | ] as const; |
| 514 | 514 | |
| 515 | −export interface RelayClientPrincipalShape { | |
| 516 | − readonly userId: string; | |
| 517 | − readonly token: string; | |
| 518 | − readonly proofKeyThumbprint?: string; | |
| 519 | − readonly dpopScopes?: ReadonlyArray<RelayDpopAccessTokenScope>; | |
| 520 | −} | |
| 521 | − | |
| 522 | 515 | export class RelayClientPrincipal extends Context.Service< |
| 523 | 516 | RelayClientPrincipal, |
| 524 | − RelayClientPrincipalShape | |
| 517 | + { | |
| 518 | + readonly userId: string; | |
| 519 | + readonly token: string; | |
| 520 | + readonly proofKeyThumbprint?: string; | |
| 521 | + readonly dpopScopes?: ReadonlyArray<RelayDpopAccessTokenScope>; | |
| 522 | + } | |
| 525 | 523 | >()("@t3tools/contracts/relay/RelayClientPrincipal") {} |
| 526 | 524 | |
| 527 | −export interface RelayEnvironmentPrincipalShape { | |
| 528 | − readonly environmentId: string; | |
| 529 | − readonly environmentPublicKey: string; | |
| 530 | −} | |
| 531 | − | |
| 532 | 525 | export class RelayEnvironmentPrincipal extends Context.Service< |
| 533 | 526 | RelayEnvironmentPrincipal, |
| 534 | − RelayEnvironmentPrincipalShape | |
| 527 | + { | |
| 528 | + readonly environmentId: string; | |
| 529 | + readonly environmentPublicKey: string; | |
| 530 | + } | |
| 535 | 531 | >()("@t3tools/contracts/relay/RelayEnvironmentPrincipal") {} |
| 536 | 532 | |
| 537 | 533 | const RelayClientBearerAuthorization = HttpApiSecurity.http({ scheme: "bearer" }).pipe( |
| @@ -6,6 +6,7 @@ import { VcsError } from "./vcs.ts"; · 5 unmodified lines | ||
| 6 | 6 | export const ReviewDiffPreviewInput = Schema.Struct({ |
| 7 | 7 | cwd: TrimmedNonEmptyString, |
| 8 | 8 | baseRef: Schema.optional(TrimmedNonEmptyString), |
| 9 | + ignoreWhitespace: Schema.optionalKey(Schema.Boolean), | |
| 9 | 10 | }); |
| 10 | 11 | export type ReviewDiffPreviewInput = typeof ReviewDiffPreviewInput.Type; |
| 11 | 12 | |
| @@ -108,6 +108,7 @@ import { · 107 unmodified lines | ||
| 108 | 108 | import { |
| 109 | 109 | PreviewAutomationError, |
| 110 | 110 | PreviewAutomationOwner, |
| 111 | + PreviewAutomationOwnerIdentity, | |
| 111 | 112 | PreviewAutomationRequest, |
| 112 | 113 | PreviewAutomationResponse, |
| 113 | 114 | } from "./previewAutomation.ts"; |
| @@ -549,7 +550,7 @@ export const WsPreviewReportStatusRpc = Rpc.make(WS_METHODS.previewReportStatus, · 435 unmodified lines | ||
| 549 | 550 | }); |
| 550 | 551 | |
| 551 | 552 | export const WsPreviewAutomationConnectRpc = Rpc.make(WS_METHODS.previewAutomationConnect, { |
| 552 | − payload: Schema.Struct({ clientId: Schema.String }), | |
| 553 | + payload: PreviewAutomationOwner, | |
| 553 | 554 | success: PreviewAutomationRequest, |
| 554 | 555 | error: Schema.Union([PreviewAutomationError, EnvironmentAuthorizationError]), |
| 555 | 556 | stream: true, |
| @@ -566,7 +567,7 @@ export const WsPreviewAutomationReportOwnerRpc = Rpc.make(WS_METHODS.previewAuto · 10 unmodified lines | ||
| 566 | 567 | }); |
| 567 | 568 | |
| 568 | 569 | export const WsPreviewAutomationClearOwnerRpc = Rpc.make(WS_METHODS.previewAutomationClearOwner, { |
| 569 | − payload: Schema.Struct({ clientId: Schema.String }), | |
| 570 | + payload: PreviewAutomationOwnerIdentity, | |
| 570 | 571 | error: Schema.Union([PreviewAutomationError, EnvironmentAuthorizationError]), |
| 571 | 572 | }); |
| 572 | 573 | |
| @@ -194,10 +194,10 @@ importers: · 193 unmodified lines | ||
| 194 | 194 | dependencies: |
| 195 | 195 | '@callstack/liquid-glass': |
| 196 | 196 | specifier: ^0.7.1 |
| 197 | − version: 0.7.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 197 | + version: 0.7.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 198 | 198 | '@clerk/expo': |
| 199 | 199 | specifier: 3.4.8-snapshot.v20260619001138 |
| 200 | − version: 3.4.8-snapshot.v20260619001138(expo-auth-session@56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(expo-constants@56.0.16)(expo-crypto@56.0.4(expo@56.0.8))(expo-secure-store@56.0.4(expo@56.0.8))(expo-web-browser@56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 200 | + version: 3.4.8-snapshot.v20260619001138(expo-auth-session@56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(expo-constants@56.0.16)(expo-crypto@56.0.4(expo@56.0.8))(expo-secure-store@56.0.4(expo@56.0.8))(expo-web-browser@56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 201 | 201 | '@effect/atom-react': |
| 202 | 202 | specifier: 4.0.0-beta.78 |
| 203 | 203 | version: 4.0.0-beta.78(effect@4.0.0-beta.78(patch_hash=c502bc684210b707dfceb87d8fe6ad6843395af6e19cfc02cd65854898bde2c5))(react@19.2.3)(scheduler@0.27.0) |
| @@ -206,10 +206,10 @@ importers: · 2 unmodified lines | ||
| 206 | 206 | version: 0.4.2 |
| 207 | 207 | '@expo/ui': |
| 208 | 208 | specifier: ~56.0.8 |
| 209 | − version: 56.0.15(961c4aa6f32829b318e3c87ef20ad401) | |
| 209 | + version: 56.0.15(ab3f255d102c60ba0fd2bbe4e47ba584) | |
| 210 | 210 | '@legendapp/list': |
| 211 | 211 | specifier: 3.0.0-beta.44 |
| 212 | − version: 3.0.0-beta.44(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 212 | + version: 3.0.0-beta.44(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 213 | 213 | '@noble/curves': |
| 214 | 214 | specifier: 'catalog:' |
| 215 | 215 | version: 1.9.1 |
| @@ -221,7 +221,7 @@ importers: · 5 unmodified lines | ||
| 221 | 221 | version: 1.3.0-beta.5(patch_hash=7cb6da88544119adda056b2f46f43956f99326227732da0b345081e285a6c53a)(@shikijs/themes@4.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) |
| 222 | 222 | '@react-native-menu/menu': |
| 223 | 223 | specifier: ^2.0.0 |
| 224 | − version: 2.0.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 224 | + version: 2.0.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 225 | 225 | '@shikijs/core': |
| 226 | 226 | specifier: 4.2.0 |
| 227 | 227 | version: 4.2.0 |
| @@ -242,7 +242,7 @@ importers: · 14 unmodified lines | ||
| 242 | 242 | version: link:../../packages/contracts |
| 243 | 243 | '@t3tools/mobile-markdown-text': |
| 244 | 244 | specifier: file:./modules/t3-markdown-text |
| 245 | − version: file:apps/mobile/modules/t3-markdown-text(f0ff241d48c23db461fff5d47e450578) | |
| 245 | + version: file:apps/mobile/modules/t3-markdown-text(e909ee9a8f7fbe234da80c739fca4984) | |
| 246 | 246 | '@t3tools/mobile-review-diff-native': |
| 247 | 247 | specifier: file:./modules/t3-review-diff |
| 248 | 248 | version: file:apps/mobile/modules/t3-review-diff |
| @@ -263,40 +263,40 @@ importers: · 14 unmodified lines | ||
| 263 | 263 | version: 4.0.0-beta.78(patch_hash=c502bc684210b707dfceb87d8fe6ad6843395af6e19cfc02cd65854898bde2c5) |
| 264 | 264 | expo: |
| 265 | 265 | specifier: ^56.0.0 |
| 266 | − version: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 266 | + version: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 267 | 267 | expo-asset: |
| 268 | 268 | specifier: ~56.0.15 |
| 269 | − version: 56.0.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3) | |
| 269 | + version: 56.0.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3) | |
| 270 | 270 | expo-auth-session: |
| 271 | 271 | specifier: ~56.0.12 |
| 272 | − version: 56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 272 | + version: 56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 273 | 273 | expo-build-properties: |
| 274 | 274 | specifier: ~56.0.15 |
| 275 | 275 | version: 56.0.16(expo@56.0.8) |
| 276 | 276 | expo-camera: |
| 277 | 277 | specifier: ~56.0.7 |
| 278 | − version: 56.0.7(@types/emscripten@1.41.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 278 | + version: 56.0.7(@types/emscripten@1.41.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 279 | 279 | expo-clipboard: |
| 280 | 280 | specifier: ~56.0.3 |
| 281 | − version: 56.0.3(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 281 | + version: 56.0.3(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 282 | 282 | expo-constants: |
| 283 | 283 | specifier: ~56.0.16 |
| 284 | − version: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 284 | + version: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 285 | 285 | expo-crypto: |
| 286 | 286 | specifier: ~56.0.4 |
| 287 | 287 | version: 56.0.4(expo@56.0.8) |
| 288 | 288 | expo-dev-client: |
| 289 | 289 | specifier: ~56.0.16 |
| 290 | − version: 56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 290 | + version: 56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 291 | 291 | expo-file-system: |
| 292 | 292 | specifier: ~56.0.7 |
| 293 | − version: 56.0.7(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 293 | + version: 56.0.7(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 294 | 294 | expo-font: |
| 295 | 295 | specifier: ~56.0.5 |
| 296 | − version: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 296 | + version: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 297 | 297 | expo-glass-effect: |
| 298 | 298 | specifier: ~56.0.4 |
| 299 | − version: 56.0.4(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 299 | + version: 56.0.4(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 300 | 300 | expo-haptics: |
| 301 | 301 | specifier: ~56.0.3 |
| 302 | 302 | version: 56.0.3(expo@56.0.8) |
| @@ -305,19 +305,19 @@ importers: · 2 unmodified lines | ||
| 305 | 305 | version: 56.0.15(expo@56.0.8) |
| 306 | 306 | expo-linking: |
| 307 | 307 | specifier: ~56.0.12 |
| 308 | − version: 56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 308 | + version: 56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 309 | 309 | expo-network: |
| 310 | 310 | specifier: ~56.0.5 |
| 311 | 311 | version: 56.0.5(expo@56.0.8)(react@19.2.3) |
| 312 | 312 | expo-notifications: |
| 313 | 313 | specifier: ~56.0.14 |
| 314 | − version: 56.0.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3) | |
| 314 | + version: 56.0.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3) | |
| 315 | 315 | expo-paste-input: |
| 316 | 316 | specifier: ^0.1.15 |
| 317 | − version: 0.1.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 317 | + version: 0.1.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 318 | 318 | expo-router: |
| 319 | 319 | specifier: ~56.2.7 |
| 320 | − version: 56.2.8(5bfdf39b8f760e4dc2d5c3acffc97310) | |
| 320 | + version: 56.2.8(c021de11d02907bd585610408f5252e8) | |
| 321 | 321 | expo-secure-store: |
| 322 | 322 | specifier: ~56.0.4 |
| 323 | 323 | version: 56.0.4(expo@56.0.8) |
| @@ -326,16 +326,16 @@ importers: · 2 unmodified lines | ||
| 326 | 326 | version: 56.0.10(expo@56.0.8)(typescript@6.0.3) |
| 327 | 327 | expo-symbols: |
| 328 | 328 | specifier: ~56.0.5 |
| 329 | − version: 56.0.5(expo-font@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 329 | + version: 56.0.5(expo-font@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 330 | 330 | expo-updates: |
| 331 | 331 | specifier: ~56.0.17 |
| 332 | − version: 56.0.17(expo-dev-client@56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 332 | + version: 56.0.17(expo-dev-client@56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 333 | 333 | expo-web-browser: |
| 334 | 334 | specifier: ~56.0.5 |
| 335 | − version: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 335 | + version: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 336 | 336 | expo-widgets: |
| 337 | 337 | specifier: ~56.0.15 |
| 338 | − version: 56.0.16(961c4aa6f32829b318e3c87ef20ad401) | |
| 338 | + version: 56.0.16(ab3f255d102c60ba0fd2bbe4e47ba584) | |
| 339 | 339 | punycode: |
| 340 | 340 | specifier: ^2.3.1 |
| 341 | 341 | version: 2.3.1 |
| @@ -347,43 +347,43 @@ importers: · 5 unmodified lines | ||
| 347 | 347 | version: 19.2.3(react@19.2.3) |
| 348 | 348 | react-native: |
| 349 | 349 | specifier: 0.85.3 |
| 350 | − version: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 350 | + version: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 351 | 351 | react-native-gesture-handler: |
| 352 | 352 | specifier: ~2.31.1 |
| 353 | − version: 2.31.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 353 | + version: 2.31.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 354 | 354 | react-native-image-viewing: |
| 355 | 355 | specifier: ^0.2.2 |
| 356 | − version: 0.2.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 356 | + version: 0.2.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 357 | 357 | react-native-keyboard-controller: |
| 358 | 358 | specifier: 1.21.6 |
| 359 | − version: 1.21.6(react-native-reanimated@4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 359 | + version: 1.21.6(react-native-reanimated@4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 360 | 360 | react-native-nitro-markdown: |
| 361 | 361 | specifier: ^0.5.0 |
| 362 | − version: 0.5.8(react-native-nitro-modules@0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-svg@15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 362 | + version: 0.5.8(react-native-nitro-modules@0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-svg@15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 363 | 363 | react-native-nitro-modules: |
| 364 | 364 | specifier: ^0.35.4 |
| 365 | − version: 0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 365 | + version: 0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 366 | 366 | react-native-reanimated: |
| 367 | 367 | specifier: 4.3.1 |
| 368 | − version: 4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 368 | + version: 4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 369 | 369 | react-native-safe-area-context: |
| 370 | 370 | specifier: ~5.7.0 |
| 371 | − version: 5.7.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 371 | + version: 5.7.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 372 | 372 | react-native-screens: |
| 373 | 373 | specifier: 4.25.2 |
| 374 | − version: 4.25.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 374 | + version: 4.25.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 375 | 375 | react-native-shiki-engine: |
| 376 | 376 | specifier: ^0.3.12 |
| 377 | − version: 0.3.12(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 377 | + version: 0.3.12(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 378 | 378 | react-native-svg: |
| 379 | 379 | specifier: 15.15.4 |
| 380 | − version: 15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 380 | + version: 15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 381 | 381 | react-native-webview: |
| 382 | 382 | specifier: ^13.16.1 |
| 383 | − version: 13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 383 | + version: 13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 384 | 384 | react-native-worklets: |
| 385 | 385 | specifier: 0.8.3 |
| 386 | − version: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 386 | + version: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 387 | 387 | shiki: |
| 388 | 388 | specifier: 4.2.0 |
| 389 | 389 | version: 4.2.0 |
| @@ -392,7 +392,7 @@ importers: · 2 unmodified lines | ||
| 392 | 392 | version: 3.6.0 |
| 393 | 393 | uniwind: |
| 394 | 394 | specifier: ^1.6.2 |
| 395 | − version: 1.7.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(tailwindcss@4.3.0) | |
| 395 | + version: 1.7.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(tailwindcss@4.3.0) | |
| 396 | 396 | devDependencies: |
| 397 | 397 | '@effect/vitest': |
| 398 | 398 | specifier: 4.0.0-beta.78 |
| @@ -10839,10 +10839,10 @@ snapshots: · 10440 unmodified lines | ||
| 10839 | 10839 | optionalDependencies: |
| 10840 | 10840 | '@types/react': 19.2.16 |
| 10841 | 10841 | |
| 10842 | − '@callstack/liquid-glass@0.7.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 10842 | + '@callstack/liquid-glass@0.7.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 10843 | 10843 | dependencies: |
| 10844 | 10844 | react: 19.2.3 |
| 10845 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 10845 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 10846 | 10846 | |
| 10847 | 10847 | '@capsizecss/unpack@4.0.0': |
| 10848 | 10848 | dependencies: |
| @@ -10946,23 +10946,23 @@ snapshots: · 97 unmodified lines | ||
| 10946 | 10946 | electron-store: 8.2.0 |
| 10947 | 10947 | react-dom: 19.2.6(react@19.2.6) |
| 10948 | 10948 | |
| 10949 | − '@clerk/expo@3.4.8-snapshot.v20260619001138(expo-auth-session@56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(expo-constants@56.0.16)(expo-crypto@56.0.4(expo@56.0.8))(expo-secure-store@56.0.4(expo@56.0.8))(expo-web-browser@56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 10949 | + '@clerk/expo@3.4.8-snapshot.v20260619001138(expo-auth-session@56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(expo-constants@56.0.16)(expo-crypto@56.0.4(expo@56.0.8))(expo-secure-store@56.0.4(expo@56.0.8))(expo-web-browser@56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 10950 | 10950 | dependencies: |
| 10951 | 10951 | '@clerk/clerk-js': 6.18.2-snapshot.v20260619001138(react-dom@19.2.3(react@19.2.3))(react@19.2.3) |
| 10952 | 10952 | '@clerk/react': 6.10.4-snapshot.v20260619001138(react-dom@19.2.3(react@19.2.3))(react@19.2.3) |
| 10953 | 10953 | '@clerk/shared': 4.19.2-snapshot.v20260619001138(react-dom@19.2.3(react@19.2.3))(react@19.2.3) |
| 10954 | 10954 | base-64: 1.0.0 |
| 10955 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 10955 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 10956 | 10956 | react: 19.2.3 |
| 10957 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 10958 | − react-native-url-polyfill: 2.0.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 10957 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 10958 | + react-native-url-polyfill: 2.0.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 10959 | 10959 | tslib: 2.8.1 |
| 10960 | 10960 | optionalDependencies: |
| 10961 | − expo-auth-session: 56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 10962 | − expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 10961 | + expo-auth-session: 56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 10962 | + expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 10963 | 10963 | expo-crypto: 56.0.4(expo@56.0.8) |
| 10964 | 10964 | expo-secure-store: 56.0.4(expo@56.0.8) |
| 10965 | − expo-web-browser: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 10965 | + expo-web-browser: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 10966 | 10966 | react-dom: 19.2.3(react@19.2.3) |
| 10967 | 10967 | |
| 10968 | 10968 | '@clerk/react@6.10.4-snapshot.v20260619001138(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': |
| @@ -11529,7 +11529,7 @@ snapshots: · 560 unmodified lines | ||
| 11529 | 11529 | |
| 11530 | 11530 | '@expo-google-fonts/material-symbols@0.4.38': {} |
| 11531 | 11531 | |
| 11532 | − '@expo/cli@56.1.13(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-constants@56.0.16)(expo-font@56.0.5)(expo-router@56.2.8)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6)': | |
| 11532 | + '@expo/cli@56.1.13(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-constants@56.0.16)(expo-font@56.0.5)(expo-router@56.2.8)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6)': | |
| 11533 | 11533 | dependencies: |
| 11534 | 11534 | '@expo/code-signing-certificates': 0.0.6 |
| 11535 | 11535 | '@expo/config': 56.0.9(typescript@6.0.3) |
| @@ -11539,7 +11539,7 @@ snapshots: · 3 unmodified lines | ||
| 11539 | 11539 | '@expo/image-utils': 0.10.1(typescript@6.0.3) |
| 11540 | 11540 | '@expo/inline-modules': 0.0.10(typescript@6.0.3) |
| 11541 | 11541 | '@expo/json-file': 10.2.0 |
| 11542 | − '@expo/log-box': 56.0.12(@expo/dom-webview@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11542 | + '@expo/log-box': 56.0.12(@expo/dom-webview@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11543 | 11543 | '@expo/metro': 56.0.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) |
| 11544 | 11544 | '@expo/metro-config': 56.0.13(patch_hash=8cb08b5bb7051ed9d2dbe46a2c293c5a1e17f1bd6ddf30de27909e18c921ff46)(bufferutil@4.1.0)(expo@56.0.8)(typescript@6.0.3)(utf-8-validate@6.0.6) |
| 11545 | 11545 | '@expo/metro-file-map': 56.0.3 |
| @@ -11564,7 +11564,7 @@ snapshots: · 18 unmodified lines | ||
| 11564 | 11564 | connect: 3.7.0 |
| 11565 | 11565 | debug: 4.4.3 |
| 11566 | 11566 | dnssd-advertise: 1.1.4 |
| 11567 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 11567 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 11568 | 11568 | expo-server: 56.0.4 |
| 11569 | 11569 | fetch-nodeshim: 0.4.10 |
| 11570 | 11570 | getenv: 2.0.0 |
| @@ -11590,8 +11590,8 @@ snapshots: · 19 unmodified lines | ||
| 11590 | 11590 | ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) |
| 11591 | 11591 | zod: 3.25.76 |
| 11592 | 11592 | optionalDependencies: |
| 11593 | − expo-router: 56.2.8(5bfdf39b8f760e4dc2d5c3acffc97310) | |
| 11594 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 11593 | + expo-router: 56.2.8(c021de11d02907bd585610408f5252e8) | |
| 11594 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 11595 | 11595 | transitivePeerDependencies: |
| 11596 | 11596 | - '@expo/dom-webview' |
| 11597 | 11597 | - '@expo/metro-runtime' |
| @@ -11653,18 +11653,18 @@ snapshots: · 55 unmodified lines | ||
| 11653 | 11653 | transitivePeerDependencies: |
| 11654 | 11654 | - supports-color |
| 11655 | 11655 | |
| 11656 | − '@expo/devtools@56.0.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 11656 | + '@expo/devtools@56.0.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 11657 | 11657 | dependencies: |
| 11658 | 11658 | chalk: 4.1.2 |
| 11659 | 11659 | optionalDependencies: |
| 11660 | 11660 | react: 19.2.3 |
| 11661 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 11661 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 11662 | 11662 | |
| 11663 | − '@expo/dom-webview@56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 11663 | + '@expo/dom-webview@56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 11664 | 11664 | dependencies: |
| 11665 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 11665 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 11666 | 11666 | react: 19.2.3 |
| 11667 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 11667 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 11668 | 11668 | |
| 11669 | 11669 | '@expo/env@2.3.0': |
| 11670 | 11670 | dependencies: |
| @@ -11725,13 +11725,13 @@ snapshots: · 54 unmodified lines | ||
| 11725 | 11725 | - supports-color |
| 11726 | 11726 | - typescript |
| 11727 | 11727 | |
| 11728 | − '@expo/log-box@56.0.12(@expo/dom-webview@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 11728 | + '@expo/log-box@56.0.12(@expo/dom-webview@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 11729 | 11729 | dependencies: |
| 11730 | − '@expo/dom-webview': 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11730 | + '@expo/dom-webview': 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11731 | 11731 | anser: 1.4.10 |
| 11732 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 11732 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 11733 | 11733 | react: 19.2.3 |
| 11734 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 11734 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 11735 | 11735 | stacktrace-parser: 0.1.11 |
| 11736 | 11736 | |
| 11737 | 11737 | '@expo/metro-config@56.0.13(patch_hash=8cb08b5bb7051ed9d2dbe46a2c293c5a1e17f1bd6ddf30de27909e18c921ff46)(bufferutil@4.1.0)(expo@56.0.8)(typescript@6.0.3)(utf-8-validate@6.0.6)': |
| @@ -11761,7 +11761,7 @@ snapshots: · 23 unmodified lines | ||
| 11761 | 11761 | postcss: 8.5.15 |
| 11762 | 11762 | resolve-from: 5.0.0 |
| 11763 | 11763 | optionalDependencies: |
| 11764 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 11764 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 11765 | 11765 | transitivePeerDependencies: |
| 11766 | 11766 | - bufferutil |
| 11767 | 11767 | - supports-color |
| @@ -11779,14 +11779,14 @@ snapshots: · 11 unmodified lines | ||
| 11779 | 11779 | transitivePeerDependencies: |
| 11780 | 11780 | - supports-color |
| 11781 | 11781 | |
| 11782 | − '@expo/metro-runtime@56.0.13(@expo/log-box@56.0.12)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 11782 | + '@expo/metro-runtime@56.0.13(@expo/log-box@56.0.12)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 11783 | 11783 | dependencies: |
| 11784 | − '@expo/log-box': 56.0.12(@expo/dom-webview@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11784 | + '@expo/log-box': 56.0.12(@expo/dom-webview@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11785 | 11785 | anser: 1.4.10 |
| 11786 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 11786 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 11787 | 11787 | pretty-format: 29.7.0 |
| 11788 | 11788 | react: 19.2.3 |
| 11789 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 11789 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 11790 | 11790 | stacktrace-parser: 0.1.11 |
| 11791 | 11791 | whatwg-fetch: 3.6.20 |
| 11792 | 11792 | optionalDependencies: |
| @@ -11861,14 +11861,14 @@ snapshots: · 68 unmodified lines | ||
| 11861 | 11861 | '@expo/router-server@56.0.12(@expo/metro-runtime@56.0.13)(expo-constants@56.0.16)(expo-font@56.0.5)(expo-router@56.2.8)(expo-server@56.0.4)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': |
| 11862 | 11862 | dependencies: |
| 11863 | 11863 | debug: 4.4.3 |
| 11864 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 11865 | − expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 11866 | − expo-font: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11864 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 11865 | + expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 11866 | + expo-font: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11867 | 11867 | expo-server: 56.0.4 |
| 11868 | 11868 | react: 19.2.3 |
| 11869 | 11869 | optionalDependencies: |
| 11870 | − '@expo/metro-runtime': 56.0.13(@expo/log-box@56.0.12)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11871 | − expo-router: 56.2.8(5bfdf39b8f760e4dc2d5c3acffc97310) | |
| 11870 | + '@expo/metro-runtime': 56.0.13(@expo/log-box@56.0.12)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11871 | + expo-router: 56.2.8(c021de11d02907bd585610408f5252e8) | |
| 11872 | 11872 | react-dom: 19.2.3(react@19.2.3) |
| 11873 | 11873 | transitivePeerDependencies: |
| 11874 | 11874 | - supports-color |
| @@ -11883,18 +11883,18 @@ snapshots: · 8 unmodified lines | ||
| 11883 | 11883 | |
| 11884 | 11884 | '@expo/sudo-prompt@9.3.2': {} |
| 11885 | 11885 | |
| 11886 | − '@expo/ui@56.0.15(961c4aa6f32829b318e3c87ef20ad401)': | |
| 11886 | + '@expo/ui@56.0.15(ab3f255d102c60ba0fd2bbe4e47ba584)': | |
| 11887 | 11887 | dependencies: |
| 11888 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 11888 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 11889 | 11889 | react: 19.2.3 |
| 11890 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 11890 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 11891 | 11891 | sf-symbols-typescript: 2.2.0 |
| 11892 | 11892 | vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) |
| 11893 | 11893 | optionalDependencies: |
| 11894 | 11894 | '@babel/core': 7.29.7 |
| 11895 | 11895 | react-dom: 19.2.3(react@19.2.3) |
| 11896 | − react-native-reanimated: 4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11897 | − react-native-worklets: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11896 | + react-native-reanimated: 4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11897 | + react-native-worklets: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 11898 | 11898 | transitivePeerDependencies: |
| 11899 | 11899 | - '@types/react' |
| 11900 | 11900 | - '@types/react-dom' |
| @@ -12155,13 +12155,13 @@ snapshots: · 254 unmodified lines | ||
| 12155 | 12155 | dependencies: |
| 12156 | 12156 | jsbi: 4.3.2 |
| 12157 | 12157 | |
| 12158 | − '@legendapp/list@3.0.0-beta.44(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 12158 | + '@legendapp/list@3.0.0-beta.44(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 12159 | 12159 | dependencies: |
| 12160 | 12160 | react: 19.2.3 |
| 12161 | 12161 | use-sync-external-store: 1.6.0(react@19.2.3) |
| 12162 | 12162 | optionalDependencies: |
| 12163 | 12163 | react-dom: 19.2.3(react@19.2.3) |
| 12164 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 12164 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 12165 | 12165 | |
| 12166 | 12166 | '@legendapp/list@3.0.0-beta.44(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': |
| 12167 | 12167 | dependencies: |
| @@ -12984,15 +12984,15 @@ snapshots: · 816 unmodified lines | ||
| 12984 | 12984 | prompts: 2.4.2 |
| 12985 | 12985 | tinyexec: 1.2.4 |
| 12986 | 12986 | |
| 12987 | − '@react-native-masked-view/masked-view@0.3.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 12987 | + '@react-native-masked-view/masked-view@0.3.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 12988 | 12988 | dependencies: |
| 12989 | 12989 | react: 19.2.3 |
| 12990 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 12990 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 12991 | 12991 | |
| 12992 | − '@react-native-menu/menu@2.0.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 12992 | + '@react-native-menu/menu@2.0.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 12993 | 12993 | dependencies: |
| 12994 | 12994 | react: 19.2.3 |
| 12995 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 12995 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 12996 | 12996 | |
| 12997 | 12997 | '@react-native/assets-registry@0.85.3': {} |
| 12998 | 12998 | |
| @@ -13052,7 +13052,7 @@ snapshots: · 53 unmodified lines | ||
| 13052 | 13052 | tinyglobby: 0.2.17 |
| 13053 | 13053 | yargs: 17.7.2 |
| 13054 | 13054 | |
| 13055 | − '@react-native/community-cli-plugin@0.85.3(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(utf-8-validate@6.0.6)': | |
| 13055 | + '@react-native/community-cli-plugin@0.85.3(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(bufferutil@4.1.0)(utf-8-validate@6.0.6)': | |
| 13056 | 13056 | dependencies: |
| 13057 | 13057 | '@react-native/dev-middleware': 0.85.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) |
| 13058 | 13058 | debug: 4.4.3 |
| @@ -13062,7 +13062,7 @@ snapshots: · 3 unmodified lines | ||
| 13062 | 13062 | metro-core: 0.84.4 |
| 13063 | 13063 | semver: 7.8.1 |
| 13064 | 13064 | optionalDependencies: |
| 13065 | − '@react-native/metro-config': 0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6) | |
| 13065 | + '@react-native/metro-config': 0.85.3(@babel/core@7.29.7) | |
| 13066 | 13066 | transitivePeerDependencies: |
| 13067 | 13067 | - bufferutil |
| 13068 | 13068 | - supports-color |
| @@ -13110,7 +13110,7 @@ snapshots: · 41 unmodified lines | ||
| 13110 | 13110 | transitivePeerDependencies: |
| 13111 | 13111 | - supports-color |
| 13112 | 13112 | |
| 13113 | − '@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6)': | |
| 13113 | + '@react-native/metro-config@0.85.3(@babel/core@7.29.7)': | |
| 13114 | 13114 | dependencies: |
| 13115 | 13115 | '@react-native/js-polyfills': 0.85.3 |
| 13116 | 13116 | '@react-native/metro-babel-transformer': 0.85.3(@babel/core@7.29.7) |
| @@ -13118,18 +13118,16 @@ snapshots: · 1 unmodified line | ||
| 13118 | 13118 | metro-runtime: 0.84.4 |
| 13119 | 13119 | transitivePeerDependencies: |
| 13120 | 13120 | - '@babel/core' |
| 13121 | − - bufferutil | |
| 13122 | 13121 | - supports-color |
| 13123 | − - utf-8-validate | |
| 13124 | 13122 | |
| 13125 | 13123 | '@react-native/normalize-colors@0.85.3': {} |
| 13126 | 13124 | |
| 13127 | − '@react-native/virtualized-lists@0.85.3(@types/react@19.2.16)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 13125 | + '@react-native/virtualized-lists@0.85.3(@types/react@19.2.16)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)': | |
| 13128 | 13126 | dependencies: |
| 13129 | 13127 | invariant: 2.2.4 |
| 13130 | 13128 | nullthrows: 1.1.1 |
| 13131 | 13129 | react: 19.2.3 |
| 13132 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 13130 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 13133 | 13131 | optionalDependencies: |
| 13134 | 13132 | '@types/react': 19.2.16 |
| 13135 | 13133 | |
| @@ -13542,15 +13540,15 @@ snapshots: · 406 unmodified lines | ||
| 13542 | 13540 | dependencies: |
| 13543 | 13541 | defer-to-connect: 2.0.1 |
| 13544 | 13542 | |
| 13545 | − '@t3tools/mobile-markdown-text@file:apps/mobile/modules/t3-markdown-text(f0ff241d48c23db461fff5d47e450578)': | |
| 13543 | + '@t3tools/mobile-markdown-text@file:apps/mobile/modules/t3-markdown-text(e909ee9a8f7fbe234da80c739fca4984)': | |
| 13546 | 13544 | dependencies: |
| 13547 | − expo-asset: 56.0.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3) | |
| 13548 | − expo-clipboard: 56.0.3(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 13545 | + expo-asset: 56.0.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3) | |
| 13546 | + expo-clipboard: 56.0.3(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 13549 | 13547 | expo-haptics: 56.0.3(expo@56.0.8) |
| 13550 | − expo-symbols: 56.0.5(expo-font@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 13548 | + expo-symbols: 56.0.5(expo-font@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 13551 | 13549 | react: 19.2.3 |
| 13552 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 13553 | − react-native-nitro-markdown: 0.5.8(react-native-nitro-modules@0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-svg@15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 13550 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 13551 | + react-native-nitro-markdown: 0.5.8(react-native-nitro-modules@0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-svg@15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 13554 | 13552 | |
| 13555 | 13553 | '@t3tools/mobile-review-diff-native@file:apps/mobile/modules/t3-review-diff': {} |
| 13556 | 13554 | |
| @@ -14626,8 +14624,8 @@ snapshots: · 1069 unmodified lines | ||
| 14626 | 14624 | react-refresh: 0.14.2 |
| 14627 | 14625 | optionalDependencies: |
| 14628 | 14626 | '@babel/runtime': 7.29.7 |
| 14629 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 14630 | − expo-widgets: 56.0.16(961c4aa6f32829b318e3c87ef20ad401) | |
| 14627 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 14628 | + expo-widgets: 56.0.16(ab3f255d102c60ba0fd2bbe4e47ba584) | |
| 14631 | 14629 | transitivePeerDependencies: |
| 14632 | 14630 | - '@babel/core' |
| 14633 | 14631 | - supports-color |
| @@ -15518,29 +15516,29 @@ snapshots: · 884 unmodified lines | ||
| 15518 | 15516 | |
| 15519 | 15517 | expo-application@56.0.3(expo@56.0.8): |
| 15520 | 15518 | dependencies: |
| 15521 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15519 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15522 | 15520 | |
| 15523 | − expo-asset@56.0.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3): | |
| 15521 | + expo-asset@56.0.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3): | |
| 15524 | 15522 | dependencies: |
| 15525 | 15523 | '@expo/image-utils': 0.10.1(typescript@6.0.3) |
| 15526 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15527 | − expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15524 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15525 | + expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15528 | 15526 | react: 19.2.3 |
| 15529 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15527 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15530 | 15528 | transitivePeerDependencies: |
| 15531 | 15529 | - supports-color |
| 15532 | 15530 | - typescript |
| 15533 | 15531 | |
| 15534 | − expo-auth-session@56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15532 | + expo-auth-session@56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15535 | 15533 | dependencies: |
| 15536 | 15534 | expo-application: 56.0.3(expo@56.0.8) |
| 15537 | − expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15535 | + expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15538 | 15536 | expo-crypto: 56.0.4(expo@56.0.8) |
| 15539 | − expo-linking: 56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15540 | − expo-web-browser: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15537 | + expo-linking: 56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15538 | + expo-web-browser: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15541 | 15539 | invariant: 2.2.4 |
| 15542 | 15540 | react: 19.2.3 |
| 15543 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15541 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15544 | 15542 | transitivePeerDependencies: |
| 15545 | 15543 | - expo |
| 15546 | 15544 | - supports-color |
| @@ -15548,119 +15546,119 @@ snapshots: · 1 unmodified line | ||
| 15548 | 15546 | expo-build-properties@56.0.16(expo@56.0.8): |
| 15549 | 15547 | dependencies: |
| 15550 | 15548 | '@expo/schema-utils': 56.0.1 |
| 15551 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15549 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15552 | 15550 | resolve-from: 5.0.0 |
| 15553 | 15551 | semver: 7.8.1 |
| 15554 | 15552 | |
| 15555 | − expo-camera@56.0.7(@types/emscripten@1.41.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15553 | + expo-camera@56.0.7(@types/emscripten@1.41.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15556 | 15554 | dependencies: |
| 15557 | 15555 | barcode-detector: 3.2.0(@types/emscripten@1.41.5) |
| 15558 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15556 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15559 | 15557 | react: 19.2.3 |
| 15560 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15558 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15561 | 15559 | transitivePeerDependencies: |
| 15562 | 15560 | - '@types/emscripten' |
| 15563 | 15561 | |
| 15564 | − expo-clipboard@56.0.3(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15562 | + expo-clipboard@56.0.3(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15565 | 15563 | dependencies: |
| 15566 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15564 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15567 | 15565 | react: 19.2.3 |
| 15568 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15566 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15569 | 15567 | |
| 15570 | − expo-constants@56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15568 | + expo-constants@56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15571 | 15569 | dependencies: |
| 15572 | 15570 | '@expo/env': 2.3.0 |
| 15573 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15574 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15571 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15572 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15575 | 15573 | transitivePeerDependencies: |
| 15576 | 15574 | - supports-color |
| 15577 | 15575 | |
| 15578 | 15576 | expo-crypto@56.0.4(expo@56.0.8): |
| 15579 | 15577 | dependencies: |
| 15580 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15578 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15581 | 15579 | |
| 15582 | − expo-dev-client@56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15580 | + expo-dev-client@56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15583 | 15581 | dependencies: |
| 15584 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15585 | − expo-dev-launcher: 56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15586 | − expo-dev-menu: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15582 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15583 | + expo-dev-launcher: 56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15584 | + expo-dev-menu: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15587 | 15585 | expo-dev-menu-interface: 56.0.1(expo@56.0.8) |
| 15588 | 15586 | expo-manifests: 56.0.4(expo@56.0.8) |
| 15589 | 15587 | expo-updates-interface: 56.0.2(expo@56.0.8) |
| 15590 | 15588 | transitivePeerDependencies: |
| 15591 | 15589 | - react-native |
| 15592 | 15590 | |
| 15593 | − expo-dev-launcher@56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15591 | + expo-dev-launcher@56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15594 | 15592 | dependencies: |
| 15595 | 15593 | '@expo/schema-utils': 56.0.1 |
| 15596 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15597 | − expo-dev-menu: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15594 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15595 | + expo-dev-menu: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15598 | 15596 | expo-manifests: 56.0.4(expo@56.0.8) |
| 15599 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15597 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15600 | 15598 | |
| 15601 | 15599 | expo-dev-menu-interface@56.0.1(expo@56.0.8): |
| 15602 | 15600 | dependencies: |
| 15603 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15601 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15604 | 15602 | |
| 15605 | − expo-dev-menu@56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15603 | + expo-dev-menu@56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15606 | 15604 | dependencies: |
| 15607 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15605 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15608 | 15606 | expo-dev-menu-interface: 56.0.1(expo@56.0.8) |
| 15609 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15607 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15610 | 15608 | |
| 15611 | 15609 | expo-eas-client@56.0.1: {} |
| 15612 | 15610 | |
| 15613 | − expo-file-system@56.0.7(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15611 | + expo-file-system@56.0.7(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15614 | 15612 | dependencies: |
| 15615 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15616 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15613 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15614 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15617 | 15615 | |
| 15618 | − expo-font@56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15616 | + expo-font@56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15619 | 15617 | dependencies: |
| 15620 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15618 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15621 | 15619 | fontfaceobserver: 2.3.0 |
| 15622 | 15620 | react: 19.2.3 |
| 15623 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15621 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15624 | 15622 | |
| 15625 | − expo-glass-effect@56.0.4(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15623 | + expo-glass-effect@56.0.4(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15626 | 15624 | dependencies: |
| 15627 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15625 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15628 | 15626 | react: 19.2.3 |
| 15629 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15627 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15630 | 15628 | |
| 15631 | 15629 | expo-haptics@56.0.3(expo@56.0.8): |
| 15632 | 15630 | dependencies: |
| 15633 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15631 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15634 | 15632 | |
| 15635 | 15633 | expo-image-loader@56.0.3(expo@56.0.8): |
| 15636 | 15634 | dependencies: |
| 15637 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15635 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15638 | 15636 | |
| 15639 | 15637 | expo-image-picker@56.0.15(expo@56.0.8): |
| 15640 | 15638 | dependencies: |
| 15641 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15639 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15642 | 15640 | expo-image-loader: 56.0.3(expo@56.0.8) |
| 15643 | 15641 | |
| 15644 | 15642 | expo-json-utils@56.0.0: {} |
| 15645 | 15643 | |
| 15646 | 15644 | expo-keep-awake@56.0.3(expo@56.0.8)(react@19.2.3): |
| 15647 | 15645 | dependencies: |
| 15648 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15646 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15649 | 15647 | react: 19.2.3 |
| 15650 | 15648 | |
| 15651 | − expo-linking@56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15649 | + expo-linking@56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15652 | 15650 | dependencies: |
| 15653 | − expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15651 | + expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15654 | 15652 | invariant: 2.2.4 |
| 15655 | 15653 | react: 19.2.3 |
| 15656 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15654 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15657 | 15655 | transitivePeerDependencies: |
| 15658 | 15656 | - expo |
| 15659 | 15657 | - supports-color |
| 15660 | 15658 | |
| 15661 | 15659 | expo-manifests@56.0.4(expo@56.0.8): |
| 15662 | 15660 | dependencies: |
| 15663 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15661 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15664 | 15662 | expo-json-utils: 56.0.0 |
| 15665 | 15663 | |
| 15666 | 15664 | expo-modules-autolinking@56.0.14(typescript@6.0.3): |
| @@ -15673,66 +15671,66 @@ snapshots: · 6 unmodified lines | ||
| 15673 | 15671 | - supports-color |
| 15674 | 15672 | - typescript |
| 15675 | 15673 | |
| 15676 | − expo-modules-core@56.0.14(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15674 | + expo-modules-core@56.0.14(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15677 | 15675 | dependencies: |
| 15678 | 15676 | '@expo/expo-modules-macros-plugin': 0.0.9 |
| 15679 | − expo-modules-jsi: 56.0.7(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15677 | + expo-modules-jsi: 56.0.7(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15680 | 15678 | invariant: 2.2.4 |
| 15681 | 15679 | react: 19.2.3 |
| 15682 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15680 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15683 | 15681 | optionalDependencies: |
| 15684 | − react-native-worklets: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15682 | + react-native-worklets: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15685 | 15683 | |
| 15686 | − expo-modules-jsi@56.0.7(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15684 | + expo-modules-jsi@56.0.7(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15687 | 15685 | dependencies: |
| 15688 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15686 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15689 | 15687 | |
| 15690 | 15688 | expo-network@56.0.5(expo@56.0.8)(react@19.2.3): |
| 15691 | 15689 | dependencies: |
| 15692 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15690 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15693 | 15691 | react: 19.2.3 |
| 15694 | 15692 | |
| 15695 | − expo-notifications@56.0.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3): | |
| 15693 | + expo-notifications@56.0.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3): | |
| 15696 | 15694 | dependencies: |
| 15697 | 15695 | '@expo/image-utils': 0.10.1(typescript@6.0.3) |
| 15698 | 15696 | abort-controller: 3.0.0 |
| 15699 | 15697 | badgin: 1.2.3 |
| 15700 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15698 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15701 | 15699 | expo-application: 56.0.3(expo@56.0.8) |
| 15702 | − expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15700 | + expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15703 | 15701 | react: 19.2.3 |
| 15704 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15702 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15705 | 15703 | transitivePeerDependencies: |
| 15706 | 15704 | - supports-color |
| 15707 | 15705 | - typescript |
| 15708 | 15706 | |
| 15709 | − expo-paste-input@0.1.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15707 | + expo-paste-input@0.1.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15710 | 15708 | dependencies: |
| 15711 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15709 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15712 | 15710 | react: 19.2.3 |
| 15713 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15711 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15714 | 15712 | |
| 15715 | − expo-router@56.2.8(5bfdf39b8f760e4dc2d5c3acffc97310): | |
| 15713 | + expo-router@56.2.8(c021de11d02907bd585610408f5252e8): | |
| 15716 | 15714 | dependencies: |
| 15717 | − '@expo/log-box': 56.0.12(@expo/dom-webview@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15718 | − '@expo/metro-runtime': 56.0.13(@expo/log-box@56.0.12)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15715 | + '@expo/log-box': 56.0.12(@expo/dom-webview@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15716 | + '@expo/metro-runtime': 56.0.13(@expo/log-box@56.0.12)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15719 | 15717 | '@expo/schema-utils': 56.0.1 |
| 15720 | − '@expo/ui': 56.0.15(961c4aa6f32829b318e3c87ef20ad401) | |
| 15718 | + '@expo/ui': 56.0.15(ab3f255d102c60ba0fd2bbe4e47ba584) | |
| 15721 | 15719 | '@radix-ui/react-slot': 1.2.4(@types/react@19.2.16)(react@19.2.3) |
| 15722 | 15720 | '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) |
| 15723 | − '@react-native-masked-view/masked-view': 0.3.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15721 | + '@react-native-masked-view/masked-view': 0.3.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15724 | 15722 | '@testing-library/jest-dom': 6.9.1 |
| 15725 | 15723 | '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) |
| 15726 | 15724 | client-only: 0.0.1 |
| 15727 | 15725 | color: 4.2.3 |
| 15728 | 15726 | debug: 4.4.3 |
| 15729 | 15727 | escape-string-regexp: 4.0.0 |
| 15730 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15731 | − expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15732 | − expo-glass-effect: 56.0.4(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15733 | − expo-linking: 56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15728 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15729 | + expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15730 | + expo-glass-effect: 56.0.4(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15731 | + expo-linking: 56.0.13(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15734 | 15732 | expo-server: 56.0.4 |
| 15735 | − expo-symbols: 56.0.5(expo-font@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15733 | + expo-symbols: 56.0.5(expo-font@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15736 | 15734 | fast-deep-equal: 3.1.3 |
| 15737 | 15735 | invariant: 2.2.4 |
| 15738 | 15736 | nanoid: 3.3.12 |
| @@ -15740,18 +15738,18 @@ snapshots: · 1 unmodified line | ||
| 15740 | 15738 | react: 19.2.3 |
| 15741 | 15739 | react-fast-compare: 3.2.2 |
| 15742 | 15740 | react-is: 19.2.7 |
| 15743 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15744 | − react-native-drawer-layout: 4.2.4(0e9729601f58a7a7ae26c76fe6017455) | |
| 15745 | − react-native-safe-area-context: 5.7.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15746 | − react-native-screens: 4.25.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15741 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15742 | + react-native-drawer-layout: 4.2.4(react-native-gesture-handler@2.31.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-reanimated@4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15743 | + react-native-safe-area-context: 5.7.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15744 | + react-native-screens: 4.25.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15747 | 15745 | server-only: 0.0.1 |
| 15748 | 15746 | sf-symbols-typescript: 2.2.0 |
| 15749 | 15747 | shallowequal: 1.1.0 |
| 15750 | 15748 | vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) |
| 15751 | 15749 | optionalDependencies: |
| 15752 | 15750 | react-dom: 19.2.3(react@19.2.3) |
| 15753 | − react-native-gesture-handler: 2.31.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15754 | − react-native-reanimated: 4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15751 | + react-native-gesture-handler: 2.31.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15752 | + react-native-reanimated: 4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15755 | 15753 | transitivePeerDependencies: |
| 15756 | 15754 | - '@babel/core' |
| 15757 | 15755 | - '@testing-library/dom' |
| @@ -15763,7 +15761,7 @@ snapshots: · 5 unmodified lines | ||
| 15763 | 15761 | |
| 15764 | 15762 | expo-secure-store@56.0.4(expo@56.0.8): |
| 15765 | 15763 | dependencies: |
| 15766 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15764 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15767 | 15765 | |
| 15768 | 15766 | expo-server@56.0.4: {} |
| 15769 | 15767 | |
| @@ -15771,7 +15769,7 @@ snapshots: · 1 unmodified line | ||
| 15771 | 15769 | dependencies: |
| 15772 | 15770 | '@expo/config-plugins': 56.0.8(typescript@6.0.3) |
| 15773 | 15771 | '@expo/image-utils': 0.10.1(typescript@6.0.3) |
| 15774 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15772 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15775 | 15773 | xml2js: 0.6.0 |
| 15776 | 15774 | transitivePeerDependencies: |
| 15777 | 15775 | - supports-color |
| @@ -15779,20 +15777,20 @@ snapshots: · 1 unmodified line | ||
| 15779 | 15777 | |
| 15780 | 15778 | expo-structured-headers@56.0.0: {} |
| 15781 | 15779 | |
| 15782 | − expo-symbols@56.0.5(expo-font@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15780 | + expo-symbols@56.0.5(expo-font@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15783 | 15781 | dependencies: |
| 15784 | 15782 | '@expo-google-fonts/material-symbols': 0.4.38 |
| 15785 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15786 | − expo-font: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15783 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15784 | + expo-font: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15787 | 15785 | react: 19.2.3 |
| 15788 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15786 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15789 | 15787 | sf-symbols-typescript: 2.2.0 |
| 15790 | 15788 | |
| 15791 | 15789 | expo-updates-interface@56.0.2(expo@56.0.8): |
| 15792 | 15790 | dependencies: |
| 15793 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15791 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15794 | 15792 | |
| 15795 | − expo-updates@56.0.17(expo-dev-client@56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15793 | + expo-updates@56.0.17(expo-dev-client@56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 15796 | 15794 | dependencies: |
| 15797 | 15795 | '@expo/code-signing-certificates': 0.0.6 |
| 15798 | 15796 | '@expo/plist': 0.7.0 |
| @@ -15800,7 +15798,7 @@ snapshots: · 1 unmodified line | ||
| 15800 | 15798 | arg: 4.1.3 |
| 15801 | 15799 | chalk: 4.1.2 |
| 15802 | 15800 | debug: 4.4.3 |
| 15803 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15801 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15804 | 15802 | expo-eas-client: 56.0.1 |
| 15805 | 15803 | expo-manifests: 56.0.4(expo@56.0.8) |
| 15806 | 15804 | expo-structured-headers: 56.0.0 |
| @@ -15810,25 +15808,25 @@ snapshots: · 3 unmodified lines | ||
| 15810 | 15808 | ignore: 5.3.2 |
| 15811 | 15809 | nullthrows: 1.1.1 |
| 15812 | 15810 | react: 19.2.3 |
| 15813 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15811 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15814 | 15812 | resolve-from: 5.0.0 |
| 15815 | 15813 | optionalDependencies: |
| 15816 | − expo-dev-client: 56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15814 | + expo-dev-client: 56.0.18(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15817 | 15815 | transitivePeerDependencies: |
| 15818 | 15816 | - supports-color |
| 15819 | 15817 | |
| 15820 | − expo-web-browser@56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15818 | + expo-web-browser@56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 15821 | 15819 | dependencies: |
| 15822 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15823 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15820 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15821 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15824 | 15822 | |
| 15825 | − expo-widgets@56.0.16(961c4aa6f32829b318e3c87ef20ad401): | |
| 15823 | + expo-widgets@56.0.16(ab3f255d102c60ba0fd2bbe4e47ba584): | |
| 15826 | 15824 | dependencies: |
| 15827 | 15825 | '@expo/plist': 0.7.0 |
| 15828 | − '@expo/ui': 56.0.15(961c4aa6f32829b318e3c87ef20ad401) | |
| 15829 | − expo: 56.0.8(63f7aade424ad9e7b1154b679fa2a14d) | |
| 15826 | + '@expo/ui': 56.0.15(ab3f255d102c60ba0fd2bbe4e47ba584) | |
| 15827 | + expo: 56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15830 | 15828 | react: 19.2.3 |
| 15831 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15829 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15832 | 15830 | transitivePeerDependencies: |
| 15833 | 15831 | - '@babel/core' |
| 15834 | 15832 | - '@types/react' |
| @@ -15837,37 +15835,37 @@ snapshots: · 2 unmodified lines | ||
| 15837 | 15835 | - react-native-reanimated |
| 15838 | 15836 | - react-native-worklets |
| 15839 | 15837 | |
| 15840 | − expo@56.0.8(63f7aade424ad9e7b1154b679fa2a14d): | |
| 15838 | + expo@56.0.8(@babel/core@7.29.7)(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-router@56.2.8)(expo-widgets@56.0.16)(react-dom@19.2.3(react@19.2.3))(react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6): | |
| 15841 | 15839 | dependencies: |
| 15842 | 15840 | '@babel/runtime': 7.29.7 |
| 15843 | − '@expo/cli': 56.1.13(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-constants@56.0.16)(expo-font@56.0.5)(expo-router@56.2.8)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15841 | + '@expo/cli': 56.1.13(@expo/dom-webview@56.0.5)(@expo/metro-runtime@56.0.13)(bufferutil@4.1.0)(expo-constants@56.0.16)(expo-font@56.0.5)(expo-router@56.2.8)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)(utf-8-validate@6.0.6) | |
| 15844 | 15842 | '@expo/config': 56.0.9(typescript@6.0.3) |
| 15845 | 15843 | '@expo/config-plugins': 56.0.8(typescript@6.0.3) |
| 15846 | − '@expo/devtools': 56.0.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15844 | + '@expo/devtools': 56.0.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15847 | 15845 | '@expo/fingerprint': 0.19.3 |
| 15848 | 15846 | '@expo/local-build-cache-provider': 56.0.8(typescript@6.0.3) |
| 15849 | − '@expo/log-box': 56.0.12(@expo/dom-webview@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15847 | + '@expo/log-box': 56.0.12(@expo/dom-webview@56.0.5)(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15850 | 15848 | '@expo/metro': 56.0.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) |
| 15851 | 15849 | '@expo/metro-config': 56.0.13(patch_hash=8cb08b5bb7051ed9d2dbe46a2c293c5a1e17f1bd6ddf30de27909e18c921ff46)(bufferutil@4.1.0)(expo@56.0.8)(typescript@6.0.3)(utf-8-validate@6.0.6) |
| 15852 | 15850 | '@ungap/structured-clone': 1.3.1 |
| 15853 | 15851 | babel-preset-expo: 56.0.14(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo-widgets@56.0.16)(expo@56.0.8)(react-refresh@0.14.2) |
| 15854 | − expo-asset: 56.0.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3) | |
| 15855 | − expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15856 | − expo-file-system: 56.0.7(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15857 | − expo-font: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15852 | + expo-asset: 56.0.15(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3) | |
| 15853 | + expo-constants: 56.0.16(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15854 | + expo-file-system: 56.0.7(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) | |
| 15855 | + expo-font: 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15858 | 15856 | expo-keep-awake: 56.0.3(expo@56.0.8)(react@19.2.3) |
| 15859 | 15857 | expo-modules-autolinking: 56.0.14(typescript@6.0.3) |
| 15860 | − expo-modules-core: 56.0.14(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15858 | + expo-modules-core: 56.0.14(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15861 | 15859 | pretty-format: 29.7.0 |
| 15862 | 15860 | react: 19.2.3 |
| 15863 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15861 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 15864 | 15862 | react-refresh: 0.14.2 |
| 15865 | 15863 | whatwg-url-minimum: 0.1.2 |
| 15866 | 15864 | optionalDependencies: |
| 15867 | − '@expo/dom-webview': 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15868 | − '@expo/metro-runtime': 56.0.13(@expo/log-box@56.0.12)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15865 | + '@expo/dom-webview': 56.0.5(expo@56.0.8)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15866 | + '@expo/metro-runtime': 56.0.13(@expo/log-box@56.0.12)(expo@56.0.8)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15869 | 15867 | react-dom: 19.2.3(react@19.2.3) |
| 15870 | − react-native-webview: 13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15868 | + react-native-webview: 13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 15871 | 15869 | transitivePeerDependencies: |
| 15872 | 15870 | - '@babel/core' |
| 15873 | 15871 | - bufferutil |
| @@ -18285,102 +18283,102 @@ snapshots: · 2411 unmodified lines | ||
| 18285 | 18283 | transitivePeerDependencies: |
| 18286 | 18284 | - supports-color |
| 18287 | 18285 | |
| 18288 | − react-native-drawer-layout@4.2.4(0e9729601f58a7a7ae26c76fe6017455): | |
| 18286 | + react-native-drawer-layout@4.2.4(react-native-gesture-handler@2.31.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-reanimated@4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18289 | 18287 | dependencies: |
| 18290 | 18288 | color: 4.2.3 |
| 18291 | 18289 | react: 19.2.3 |
| 18292 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18293 | − react-native-gesture-handler: 2.31.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18294 | − react-native-reanimated: 4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18290 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18291 | + react-native-gesture-handler: 2.31.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18292 | + react-native-reanimated: 4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18295 | 18293 | use-latest-callback: 0.2.6(react@19.2.3) |
| 18296 | 18294 | |
| 18297 | − react-native-gesture-handler@2.31.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18295 | + react-native-gesture-handler@2.31.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18298 | 18296 | dependencies: |
| 18299 | 18297 | '@egjs/hammerjs': 2.0.17 |
| 18300 | 18298 | '@types/react-test-renderer': 19.1.0 |
| 18301 | 18299 | hoist-non-react-statics: 3.3.2 |
| 18302 | 18300 | invariant: 2.2.4 |
| 18303 | 18301 | react: 19.2.3 |
| 18304 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18302 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18305 | 18303 | |
| 18306 | − react-native-image-viewing@0.2.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18304 | + react-native-image-viewing@0.2.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18307 | 18305 | dependencies: |
| 18308 | 18306 | react: 19.2.3 |
| 18309 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18307 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18310 | 18308 | |
| 18311 | − react-native-is-edge-to-edge@1.3.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18309 | + react-native-is-edge-to-edge@1.3.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18312 | 18310 | dependencies: |
| 18313 | 18311 | react: 19.2.3 |
| 18314 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18312 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18315 | 18313 | |
| 18316 | − react-native-keyboard-controller@1.21.6(react-native-reanimated@4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18314 | + react-native-keyboard-controller@1.21.6(react-native-reanimated@4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18317 | 18315 | dependencies: |
| 18318 | 18316 | react: 19.2.3 |
| 18319 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18320 | − react-native-is-edge-to-edge: 1.3.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18321 | − react-native-reanimated: 4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18317 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18318 | + react-native-is-edge-to-edge: 1.3.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18319 | + react-native-reanimated: 4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18322 | 18320 | |
| 18323 | − react-native-nitro-markdown@0.5.8(react-native-nitro-modules@0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-svg@15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18321 | + react-native-nitro-markdown@0.5.8(react-native-nitro-modules@0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native-svg@15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18324 | 18322 | dependencies: |
| 18325 | 18323 | react: 19.2.3 |
| 18326 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18327 | − react-native-nitro-modules: 0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18324 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18325 | + react-native-nitro-modules: 0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18328 | 18326 | optionalDependencies: |
| 18329 | − react-native-svg: 15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18327 | + react-native-svg: 15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18330 | 18328 | |
| 18331 | − react-native-nitro-modules@0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18329 | + react-native-nitro-modules@0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18332 | 18330 | dependencies: |
| 18333 | 18331 | react: 19.2.3 |
| 18334 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18332 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18335 | 18333 | |
| 18336 | − react-native-reanimated@4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18334 | + react-native-reanimated@4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18337 | 18335 | dependencies: |
| 18338 | 18336 | react: 19.2.3 |
| 18339 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18340 | − react-native-is-edge-to-edge: 1.3.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18341 | − react-native-worklets: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18337 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18338 | + react-native-is-edge-to-edge: 1.3.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18339 | + react-native-worklets: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18342 | 18340 | semver: 7.8.1 |
| 18343 | 18341 | |
| 18344 | − react-native-safe-area-context@5.7.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18342 | + react-native-safe-area-context@5.7.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18345 | 18343 | dependencies: |
| 18346 | 18344 | react: 19.2.3 |
| 18347 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18345 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18348 | 18346 | |
| 18349 | − react-native-screens@4.25.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18347 | + react-native-screens@4.25.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18350 | 18348 | dependencies: |
| 18351 | 18349 | react: 19.2.3 |
| 18352 | 18350 | react-freeze: 1.0.4(react@19.2.3) |
| 18353 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18351 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18354 | 18352 | warn-once: 0.1.1 |
| 18355 | 18353 | |
| 18356 | − react-native-shiki-engine@0.3.12(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18354 | + react-native-shiki-engine@0.3.12(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18357 | 18355 | dependencies: |
| 18358 | 18356 | '@shikijs/types': 4.2.0 |
| 18359 | 18357 | '@shikijs/vscode-textmate': 10.0.2 |
| 18360 | 18358 | react: 19.2.3 |
| 18361 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18359 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18362 | 18360 | |
| 18363 | − react-native-svg@15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18361 | + react-native-svg@15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18364 | 18362 | dependencies: |
| 18365 | 18363 | css-select: 5.2.2 |
| 18366 | 18364 | css-tree: 1.1.3 |
| 18367 | 18365 | react: 19.2.3 |
| 18368 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18366 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18369 | 18367 | warn-once: 0.1.1 |
| 18370 | 18368 | |
| 18371 | − react-native-url-polyfill@2.0.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 18369 | + react-native-url-polyfill@2.0.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)): | |
| 18372 | 18370 | dependencies: |
| 18373 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18371 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18374 | 18372 | whatwg-url-without-unicode: 8.0.0-3 |
| 18375 | 18373 | |
| 18376 | − react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18374 | + react-native-webview@13.16.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18377 | 18375 | dependencies: |
| 18378 | 18376 | escape-string-regexp: 4.0.0 |
| 18379 | 18377 | invariant: 2.2.4 |
| 18380 | 18378 | react: 19.2.3 |
| 18381 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18379 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18382 | 18380 | |
| 18383 | − react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18381 | + react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): | |
| 18384 | 18382 | dependencies: |
| 18385 | 18383 | '@babel/core': 7.29.7 |
| 18386 | 18384 | '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7) |
| @@ -18392,23 +18390,23 @@ snapshots: · 5 unmodified lines | ||
| 18392 | 18390 | '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7) |
| 18393 | 18391 | '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7) |
| 18394 | 18392 | '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) |
| 18395 | − '@react-native/metro-config': 0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6) | |
| 18393 | + '@react-native/metro-config': 0.85.3(@babel/core@7.29.7) | |
| 18396 | 18394 | convert-source-map: 2.0.0 |
| 18397 | 18395 | react: 19.2.3 |
| 18398 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18396 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 18399 | 18397 | semver: 7.8.1 |
| 18400 | 18398 | transitivePeerDependencies: |
| 18401 | 18399 | - supports-color |
| 18402 | 18400 | |
| 18403 | − react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6): | |
| 18401 | + react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6): | |
| 18404 | 18402 | dependencies: |
| 18405 | 18403 | '@react-native/assets-registry': 0.85.3 |
| 18406 | 18404 | '@react-native/codegen': 0.85.3(@babel/core@7.29.7) |
| 18407 | − '@react-native/community-cli-plugin': 0.85.3(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(utf-8-validate@6.0.6) | |
| 18405 | + '@react-native/community-cli-plugin': 0.85.3(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(bufferutil@4.1.0)(utf-8-validate@6.0.6) | |
| 18408 | 18406 | '@react-native/gradle-plugin': 0.85.3 |
| 18409 | 18407 | '@react-native/js-polyfills': 0.85.3 |
| 18410 | 18408 | '@react-native/normalize-colors': 0.85.3 |
| 18411 | − '@react-native/virtualized-lists': 0.85.3(@types/react@19.2.16)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18409 | + '@react-native/virtualized-lists': 0.85.3(@types/react@19.2.16)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) | |
| 18412 | 18410 | abort-controller: 3.0.0 |
| 18413 | 18411 | anser: 1.4.10 |
| 18414 | 18412 | ansi-regex: 5.0.1 |
| @@ -19437,14 +19435,14 @@ snapshots: · 1022 unmodified lines | ||
| 19437 | 19435 | |
| 19438 | 19436 | universalify@2.0.1: {} |
| 19439 | 19437 | |
| 19440 | − uniwind@1.7.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(tailwindcss@4.3.0): | |
| 19438 | + uniwind@1.7.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(tailwindcss@4.3.0): | |
| 19441 | 19439 | dependencies: |
| 19442 | 19440 | '@tailwindcss/node': 4.2.1 |
| 19443 | 19441 | '@tailwindcss/oxide': 4.2.1 |
| 19444 | 19442 | culori: 4.0.2 |
| 19445 | 19443 | lightningcss: 1.30.1 |
| 19446 | 19444 | react: 19.2.3 |
| 19447 | − react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 19445 | + react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) | |
| 19448 | 19446 | tailwindcss: 4.3.0 |
| 19449 | 19447 | |
| 19450 | 19448 | unpipe@1.0.0: {} |