| author | |
| committer | |
| log | 9b43123ba2a90ecaaf2eac1f75b6aebcf181f253 |
| tree | 40fd2cc58603d6ca9504bf3f36136e8bebb53e39 |
| parent | 17fb0e4a9a068f3ba6c55871dd44ed3c5d70aefe |
Co-authored-by: codex <codex@users.noreply.github.com>30 files changed, 381 insertions(+), 163 deletions(-)
apps/desktop/src/ipc/methods/preview.ts+42-14| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | // @effect-diagnostics nodeBuiltinImport:off | ||
| 2 | import * as Data from "effect/Data"; | ||
| 1 | import * as Effect from "effect/Effect"; | 3 | import * as Effect from "effect/Effect"; |
| 2 | import { BrowserWindow } from "electron"; | 4 | import { BrowserWindow } from "electron"; |
| 3 | import * as NodeFileSystem from "node:fs"; | ||
| 4 | import * as NodePath from "node:path"; | ||
| 5 | import { pathToFileURL } from "node:url"; | 5 | import { pathToFileURL } from "node:url"; |
| 6 | 6 | ||
| 7 | import { previewViewManager } from "../../preview-view-manager.ts"; | 7 | import { previewViewManager } from "../../preview-view-manager.ts"; |
| ... | @@ -29,18 +29,38 @@ const tabIdFrom = (raw: unknown): string => { | ... | @@ -29,18 +29,38 @@ const tabIdFrom = (raw: unknown): string => { |
| 29 | return tabId; | 29 | return tabId; |
| 30 | }; | 30 | }; |
| 31 | 31 | ||
| 32 | const method = (channel: string, handler: (raw: unknown) => unknown | Promise<unknown>): DesktopIpcMethod<unknown, never> => ({ | 32 | class PreviewIpcError extends Data.TaggedError("PreviewIpcError")<{ |
| 33 | readonly cause: unknown; | ||
| 34 | }> {} | ||
| 35 | |||
| 36 | const method = ( | ||
| 37 | channel: string, | ||
| 38 | handler: (raw: unknown) => unknown | Promise<unknown>, | ||
| 39 | ): DesktopIpcMethod<PreviewIpcError, never> => ({ | ||
| 33 | channel, | 40 | channel, |
| 34 | handler: (raw) => Effect.tryPromise({ try: () => Promise.resolve(handler(raw)), catch: (error) => error }), | 41 | handler: (raw) => |
| 42 | Effect.tryPromise({ | ||
| 43 | try: () => Promise.resolve(handler(raw)), | ||
| 44 | catch: (cause) => new PreviewIpcError({ cause }), | ||
| 45 | }), | ||
| 35 | }); | 46 | }); |
| 36 | 47 | ||
| 37 | export const previewMethods = [ | 48 | export const previewMethods = [ |
| 38 | method(IpcChannels.PREVIEW_CREATE_TAB_CHANNEL, (raw) => previewViewManager.createTab(tabIdFrom(raw))), | 49 | method(IpcChannels.PREVIEW_CREATE_TAB_CHANNEL, (raw) => |
| 39 | method(IpcChannels.PREVIEW_CLOSE_TAB_CHANNEL, (raw) => previewViewManager.closeTab(tabIdFrom(raw))), | 50 | previewViewManager.createTab(tabIdFrom(raw)), |
| 51 | ), | ||
| 52 | method(IpcChannels.PREVIEW_CLOSE_TAB_CHANNEL, (raw) => | ||
| 53 | previewViewManager.closeTab(tabIdFrom(raw)), | ||
| 54 | ), | ||
| 40 | method(IpcChannels.PREVIEW_REGISTER_WEBVIEW_CHANNEL, (raw) => { | 55 | method(IpcChannels.PREVIEW_REGISTER_WEBVIEW_CHANNEL, (raw) => { |
| 41 | const tabId = tabIdFrom(raw); | 56 | const tabId = tabIdFrom(raw); |
| 42 | const webContentsId = typeof raw === "object" && raw !== null && "webContentsId" in raw ? raw.webContentsId : null; | 57 | const webContentsId = |
| 43 | if (typeof webContentsId !== "number" || !Number.isInteger(webContentsId) || webContentsId <= 0) { | 58 | typeof raw === "object" && raw !== null && "webContentsId" in raw ? raw.webContentsId : null; |
| 59 | if ( | ||
| 60 | typeof webContentsId !== "number" || | ||
| 61 | !Number.isInteger(webContentsId) || | ||
| 62 | webContentsId <= 0 | ||
| 63 | ) { | ||
| 44 | throw new Error("preview webContentsId must be a positive integer"); | 64 | throw new Error("preview webContentsId must be a positive integer"); |
| 45 | } | 65 | } |
| 46 | return previewViewManager.registerWebview(tabId, webContentsId); | 66 | return previewViewManager.registerWebview(tabId, webContentsId); |
| ... | @@ -52,21 +72,29 @@ export const previewMethods = [ | ... | @@ -52,21 +72,29 @@ export const previewMethods = [ |
| 52 | return previewViewManager.navigate(tabId, url); | 72 | return previewViewManager.navigate(tabId, url); |
| 53 | }), | 73 | }), |
| 54 | method(IpcChannels.PREVIEW_GO_BACK_CHANNEL, (raw) => previewViewManager.goBack(tabIdFrom(raw))), | 74 | method(IpcChannels.PREVIEW_GO_BACK_CHANNEL, (raw) => previewViewManager.goBack(tabIdFrom(raw))), |
| 55 | method(IpcChannels.PREVIEW_GO_FORWARD_CHANNEL, (raw) => previewViewManager.goForward(tabIdFrom(raw))), | 75 | method(IpcChannels.PREVIEW_GO_FORWARD_CHANNEL, (raw) => |
| 76 | previewViewManager.goForward(tabIdFrom(raw)), | ||
| 77 | ), | ||
| 56 | method(IpcChannels.PREVIEW_REFRESH_CHANNEL, (raw) => previewViewManager.refresh(tabIdFrom(raw))), | 78 | method(IpcChannels.PREVIEW_REFRESH_CHANNEL, (raw) => previewViewManager.refresh(tabIdFrom(raw))), |
| 57 | method(IpcChannels.PREVIEW_ZOOM_IN_CHANNEL, (raw) => previewViewManager.zoomIn(tabIdFrom(raw))), | 79 | method(IpcChannels.PREVIEW_ZOOM_IN_CHANNEL, (raw) => previewViewManager.zoomIn(tabIdFrom(raw))), |
| 58 | method(IpcChannels.PREVIEW_ZOOM_OUT_CHANNEL, (raw) => previewViewManager.zoomOut(tabIdFrom(raw))), | 80 | method(IpcChannels.PREVIEW_ZOOM_OUT_CHANNEL, (raw) => previewViewManager.zoomOut(tabIdFrom(raw))), |
| 59 | method(IpcChannels.PREVIEW_RESET_ZOOM_CHANNEL, (raw) => previewViewManager.resetZoom(tabIdFrom(raw))), | 81 | method(IpcChannels.PREVIEW_RESET_ZOOM_CHANNEL, (raw) => |
| 60 | method(IpcChannels.PREVIEW_HARD_RELOAD_CHANNEL, (raw) => previewViewManager.hardReload(tabIdFrom(raw))), | 82 | previewViewManager.resetZoom(tabIdFrom(raw)), |
| 61 | method(IpcChannels.PREVIEW_OPEN_DEVTOOLS_CHANNEL, (raw) => previewViewManager.openDevTools(tabIdFrom(raw))), | 83 | ), |
| 84 | method(IpcChannels.PREVIEW_HARD_RELOAD_CHANNEL, (raw) => | ||
| 85 | previewViewManager.hardReload(tabIdFrom(raw)), | ||
| 86 | ), | ||
| 87 | method(IpcChannels.PREVIEW_OPEN_DEVTOOLS_CHANNEL, (raw) => | ||
| 88 | previewViewManager.openDevTools(tabIdFrom(raw)), | ||
| 89 | ), | ||
| 62 | method(IpcChannels.PREVIEW_CLEAR_COOKIES_CHANNEL, () => previewViewManager.clearCookies()), | 90 | method(IpcChannels.PREVIEW_CLEAR_COOKIES_CHANNEL, () => previewViewManager.clearCookies()), |
| 63 | method(IpcChannels.PREVIEW_CLEAR_CACHE_CHANNEL, () => previewViewManager.clearCache()), | 91 | method(IpcChannels.PREVIEW_CLEAR_CACHE_CHANNEL, () => previewViewManager.clearCache()), |
| 64 | method(IpcChannels.PREVIEW_GET_CONFIG_CHANNEL, () => { | 92 | method(IpcChannels.PREVIEW_GET_CONFIG_CHANNEL, () => { |
| 65 | const preloadPath = NodePath.join(__dirname, "preview-pick-preload.cjs"); | 93 | const preloadPath = `${__dirname}/preview-pick-preload.cjs`; |
| 66 | return { | 94 | return { |
| 67 | partition: previewViewManager.getBrowserPartition(), | 95 | partition: previewViewManager.getBrowserPartition(), |
| 68 | webPreferences: PREVIEW_WEBVIEW_PREFERENCES, | 96 | webPreferences: PREVIEW_WEBVIEW_PREFERENCES, |
| 69 | preloadUrl: NodeFileSystem.existsSync(preloadPath) ? pathToFileURL(preloadPath).href : null, | 97 | preloadUrl: pathToFileURL(preloadPath).href, |
| 70 | }; | 98 | }; |
| 71 | }), | 99 | }), |
| 72 | method(IpcChannels.PREVIEW_PICK_ELEMENT_CHANNEL, (raw) => | 100 | method(IpcChannels.PREVIEW_PICK_ELEMENT_CHANNEL, (raw) => |
apps/desktop/src/main.ts-2| ... | @@ -28,7 +28,6 @@ import * as DesktopApp from "./app/DesktopApp.ts"; | ... | @@ -28,7 +28,6 @@ import * as DesktopApp from "./app/DesktopApp.ts"; |
| 28 | import * as DesktopAppIdentity from "./app/DesktopAppIdentity.ts"; | 28 | import * as DesktopAppIdentity from "./app/DesktopAppIdentity.ts"; |
| 29 | import * as DesktopCloudAuth from "./app/DesktopCloudAuth.ts"; | 29 | import * as DesktopCloudAuth from "./app/DesktopCloudAuth.ts"; |
| 30 | import * as DesktopCloudAuthTokenStore from "./app/DesktopCloudAuthTokenStore.ts"; | 30 | import * as DesktopCloudAuthTokenStore from "./app/DesktopCloudAuthTokenStore.ts"; |
| 31 | import * as DesktopConnectionCatalogStore from "./app/DesktopConnectionCatalogStore.ts"; | ||
| 32 | import * as DesktopApplicationMenu from "./window/DesktopApplicationMenu.ts"; | 31 | import * as DesktopApplicationMenu from "./window/DesktopApplicationMenu.ts"; |
| 33 | import * as DesktopAssets from "./app/DesktopAssets.ts"; | 32 | import * as DesktopAssets from "./app/DesktopAssets.ts"; |
| 34 | import * as DesktopBackendConfiguration from "./backend/DesktopBackendConfiguration.ts"; | 33 | import * as DesktopBackendConfiguration from "./backend/DesktopBackendConfiguration.ts"; |
| ... | @@ -115,7 +114,6 @@ const desktopFoundationLayer = Layer.mergeAll( | ... | @@ -115,7 +114,6 @@ const desktopFoundationLayer = Layer.mergeAll( |
| 115 | DesktopClientSettings.layer, | 114 | DesktopClientSettings.layer, |
| 116 | DesktopSavedEnvironments.layer, | 115 | DesktopSavedEnvironments.layer, |
| 117 | DesktopCloudAuthTokenStore.layer, | 116 | DesktopCloudAuthTokenStore.layer, |
| 118 | DesktopConnectionCatalogStore.layer, | ||
| 119 | DesktopAssets.layer, | 117 | DesktopAssets.layer, |
| 120 | DesktopObservability.layer, | 118 | DesktopObservability.layer, |
| 121 | ).pipe(Layer.provideMerge(desktopEnvironmentLayer)); | 119 | ).pipe(Layer.provideMerge(desktopEnvironmentLayer)); |
apps/desktop/src/picked-element-payload.test.ts+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | import { describe, expect, it } from "vitest"; | 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | 2 | ||
| 3 | import { isPickedElementPayload } from "./picked-element-payload.ts"; | 3 | import { isPickedElementPayload } from "./picked-element-payload.ts"; |
| 4 | 4 |
apps/desktop/src/preload.ts+3-7| ... | @@ -47,10 +47,6 @@ contextBridge.exposeInMainWorld("desktopBridge", { | ... | @@ -47,10 +47,6 @@ contextBridge.exposeInMainWorld("desktopBridge", { |
| 47 | ipcRenderer.invoke(IpcChannels.SET_SAVED_ENVIRONMENT_SECRET_CHANNEL, { environmentId, secret }), | 47 | ipcRenderer.invoke(IpcChannels.SET_SAVED_ENVIRONMENT_SECRET_CHANNEL, { environmentId, secret }), |
| 48 | removeSavedEnvironmentSecret: (environmentId) => | 48 | removeSavedEnvironmentSecret: (environmentId) => |
| 49 | ipcRenderer.invoke(IpcChannels.REMOVE_SAVED_ENVIRONMENT_SECRET_CHANNEL, environmentId), | 49 | ipcRenderer.invoke(IpcChannels.REMOVE_SAVED_ENVIRONMENT_SECRET_CHANNEL, environmentId), |
| 50 | getConnectionCatalog: () => ipcRenderer.invoke(IpcChannels.GET_CONNECTION_CATALOG_CHANNEL), | ||
| 51 | setConnectionCatalog: (catalog) => | ||
| 52 | ipcRenderer.invoke(IpcChannels.SET_CONNECTION_CATALOG_CHANNEL, catalog), | ||
| 53 | clearConnectionCatalog: () => ipcRenderer.invoke(IpcChannels.CLEAR_CONNECTION_CATALOG_CHANNEL), | ||
| 54 | discoverSshHosts: () => ipcRenderer.invoke(IpcChannels.DISCOVER_SSH_HOSTS_CHANNEL), | 50 | discoverSshHosts: () => ipcRenderer.invoke(IpcChannels.DISCOVER_SSH_HOSTS_CHANNEL), |
| 55 | ensureSshEnvironment: async (target, options) => | 51 | ensureSshEnvironment: async (target, options) => |
| 56 | unwrapEnsureSshEnvironmentResult( | 52 | unwrapEnsureSshEnvironmentResult( |
| ... | @@ -164,8 +160,7 @@ contextBridge.exposeInMainWorld("desktopBridge", { | ... | @@ -164,8 +160,7 @@ contextBridge.exposeInMainWorld("desktopBridge", { |
| 164 | clearCookies: () => ipcRenderer.invoke(IpcChannels.PREVIEW_CLEAR_COOKIES_CHANNEL), | 160 | clearCookies: () => ipcRenderer.invoke(IpcChannels.PREVIEW_CLEAR_COOKIES_CHANNEL), |
| 165 | clearCache: () => ipcRenderer.invoke(IpcChannels.PREVIEW_CLEAR_CACHE_CHANNEL), | 161 | clearCache: () => ipcRenderer.invoke(IpcChannels.PREVIEW_CLEAR_CACHE_CHANNEL), |
| 166 | getPreviewConfig: () => ipcRenderer.invoke(IpcChannels.PREVIEW_GET_CONFIG_CHANNEL), | 162 | getPreviewConfig: () => ipcRenderer.invoke(IpcChannels.PREVIEW_GET_CONFIG_CHANNEL), |
| 167 | pickElement: (tabId) => | 163 | pickElement: (tabId) => ipcRenderer.invoke(IpcChannels.PREVIEW_PICK_ELEMENT_CHANNEL, { tabId }), |
| 168 | ipcRenderer.invoke(IpcChannels.PREVIEW_PICK_ELEMENT_CHANNEL, { tabId }), | ||
| 169 | cancelPickElement: (tabId) => | 164 | cancelPickElement: (tabId) => |
| 170 | ipcRenderer.invoke(IpcChannels.PREVIEW_CANCEL_PICK_ELEMENT_CHANNEL, { tabId }), | 165 | ipcRenderer.invoke(IpcChannels.PREVIEW_CANCEL_PICK_ELEMENT_CHANNEL, { tabId }), |
| 171 | onStateChange: (listener) => { | 166 | onStateChange: (listener) => { |
| ... | @@ -178,7 +173,8 @@ contextBridge.exposeInMainWorld("desktopBridge", { | ... | @@ -178,7 +173,8 @@ contextBridge.exposeInMainWorld("desktopBridge", { |
| 178 | listener(tabId, state as DesktopPreviewTabState); | 173 | listener(tabId, state as DesktopPreviewTabState); |
| 179 | }; | 174 | }; |
| 180 | ipcRenderer.on(IpcChannels.PREVIEW_STATE_CHANGE_CHANNEL, wrappedListener); | 175 | ipcRenderer.on(IpcChannels.PREVIEW_STATE_CHANGE_CHANNEL, wrappedListener); |
| 181 | return () => ipcRenderer.removeListener(IpcChannels.PREVIEW_STATE_CHANGE_CHANNEL, wrappedListener); | 176 | return () => |
| 177 | ipcRenderer.removeListener(IpcChannels.PREVIEW_STATE_CHANGE_CHANNEL, wrappedListener); | ||
| 182 | }, | 178 | }, |
| 183 | }, | 179 | }, |
| 184 | } satisfies DesktopBridge); | 180 | } satisfies DesktopBridge); |
apps/desktop/src/preview-pick-preload.test.ts+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | import { describe, expect, it } from "vitest"; | 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | 2 | ||
| 3 | import { computeLabelPosition } from "./preview-pick-label-position.ts"; | 3 | import { computeLabelPosition } from "./preview-pick-label-position.ts"; |
| 4 | 4 |
apps/desktop/src/preview-pick-preload.ts+1| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | // @effect-diagnostics globalDate:off | ||
| 1 | /** | 2 | /** |
| 2 | * Preview pick preload — runs in the isolated world of the Chromium | 3 | * Preview pick preload — runs in the isolated world of the Chromium |
| 3 | * `<webview>` that hosts the in-app browser. Loaded via the | 4 | * `<webview>` that hosts the in-app browser. Loaded via the |
apps/desktop/src/preview-view-manager.ts+1| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | // @effect-diagnostics globalDate:off | ||
| 1 | /** | 2 | /** |
| 2 | * PreviewViewManager — desktop side of the in-app browser preview. | 3 | * PreviewViewManager — desktop side of the in-app browser preview. |
| 3 | * | 4 | * |
apps/desktop/src/preview-webview-preferences.test.ts+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | import { describe, expect, it } from "vitest"; | 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | 2 | ||
| 3 | import { PREVIEW_WEBVIEW_PREFERENCES } from "./preview-webview-preferences.ts"; | 3 | import { PREVIEW_WEBVIEW_PREFERENCES } from "./preview-webview-preferences.ts"; |
| 4 | 4 |
apps/server/src/preview/Layers/Manager.test.ts+1-1| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | import { it } from "@effect/vitest"; | 1 | import { it } from "@effect/vitest"; |
| 2 | import { type PreviewEvent, ThreadId } from "@t3tools/contracts"; | 2 | import { type PreviewEvent, ThreadId } from "@t3tools/contracts"; |
| 3 | import { Effect, PubSub } from "effect"; | 3 | import { Effect, PubSub } from "effect"; |
| 4 | import { expect } from "vitest"; | 4 | import { expect } from "vite-plus/test"; |
| 5 | 5 | ||
| 6 | import { PreviewManager } from "../Services/Manager.ts"; | 6 | import { PreviewManager } from "../Services/Manager.ts"; |
| 7 | import { PreviewManagerLive } from "./Manager.ts"; | 7 | import { PreviewManagerLive } from "./Manager.ts"; |
apps/server/src/preview/Layers/Manager.ts+27-19| ... | @@ -21,7 +21,7 @@ import { | ... | @@ -21,7 +21,7 @@ import { |
| 21 | normalizePreviewUrl, | 21 | normalizePreviewUrl, |
| 22 | PreviewUrlNormalizationError, | 22 | PreviewUrlNormalizationError, |
| 23 | } from "@t3tools/shared/preview"; | 23 | } from "@t3tools/shared/preview"; |
| 24 | import { Effect, Layer, PubSub, Stream, SynchronizedRef } from "effect"; | 24 | import { DateTime, Effect, Layer, PubSub, Stream, SynchronizedRef } from "effect"; |
| 25 | 25 | ||
| 26 | import { PreviewManager, type PreviewManagerShape } from "../Services/Manager.ts"; | 26 | import { PreviewManager, type PreviewManagerShape } from "../Services/Manager.ts"; |
| 27 | 27 | ||
| ... | @@ -66,30 +66,34 @@ const normalizeUrl = (rawUrl: string): Effect.Effect<string, PreviewInvalidUrlEr | ... | @@ -66,30 +66,34 @@ const normalizeUrl = (rawUrl: string): Effect.Effect<string, PreviewInvalidUrlEr |
| 66 | }), | 66 | }), |
| 67 | }); | 67 | }); |
| 68 | 68 | ||
| 69 | const currentIsoTimestamp = DateTime.now.pipe(Effect.map(DateTime.formatIso)); | ||
| 70 | |||
| 69 | const buildLoadingSnapshot = (input: { | 71 | const buildLoadingSnapshot = (input: { |
| 70 | readonly threadId: string; | 72 | readonly threadId: string; |
| 71 | readonly tabId: string; | 73 | readonly tabId: string; |
| 72 | readonly url: string; | 74 | readonly url: string; |
| 73 | readonly title: string; | 75 | readonly title: string; |
| 76 | readonly updatedAt: string; | ||
| 74 | }): PreviewSessionSnapshot => ({ | 77 | }): PreviewSessionSnapshot => ({ |
| 75 | threadId: input.threadId, | 78 | threadId: input.threadId, |
| 76 | tabId: input.tabId, | 79 | tabId: input.tabId, |
| 77 | navStatus: { _tag: "Loading", url: input.url, title: input.title }, | 80 | navStatus: { _tag: "Loading", url: input.url, title: input.title }, |
| 78 | canGoBack: false, | 81 | canGoBack: false, |
| 79 | canGoForward: false, | 82 | canGoForward: false, |
| 80 | updatedAt: new Date().toISOString(), | 83 | updatedAt: input.updatedAt, |
| 81 | }); | 84 | }); |
| 82 | 85 | ||
| 83 | const buildIdleSnapshot = (input: { | 86 | const buildIdleSnapshot = (input: { |
| 84 | readonly threadId: string; | 87 | readonly threadId: string; |
| 85 | readonly tabId: string; | 88 | readonly tabId: string; |
| 89 | readonly updatedAt: string; | ||
| 86 | }): PreviewSessionSnapshot => ({ | 90 | }): PreviewSessionSnapshot => ({ |
| 87 | threadId: input.threadId, | 91 | threadId: input.threadId, |
| 88 | tabId: input.tabId, | 92 | tabId: input.tabId, |
| 89 | navStatus: { _tag: "Idle" }, | 93 | navStatus: { _tag: "Idle" }, |
| 90 | canGoBack: false, | 94 | canGoBack: false, |
| 91 | canGoForward: false, | 95 | canGoForward: false, |
| 92 | updatedAt: new Date().toISOString(), | 96 | updatedAt: input.updatedAt, |
| 93 | }); | 97 | }); |
| 94 | 98 | ||
| 95 | export const makePreviewManager = Effect.gen(function* () { | 99 | export const makePreviewManager = Effect.gen(function* () { |
| ... | @@ -152,14 +156,16 @@ export const makePreviewManager = Effect.gen(function* () { | ... | @@ -152,14 +156,16 @@ export const makePreviewManager = Effect.gen(function* () { |
| 152 | const open: PreviewManagerShape["open"] = (input) => | 156 | const open: PreviewManagerShape["open"] = (input) => |
| 153 | Effect.gen(function* () { | 157 | Effect.gen(function* () { |
| 154 | const tabId = newPreviewTabId(); | 158 | const tabId = newPreviewTabId(); |
| 159 | const updatedAt = yield* currentIsoTimestamp; | ||
| 155 | const snapshot = input.url | 160 | const snapshot = input.url |
| 156 | ? buildLoadingSnapshot({ | 161 | ? buildLoadingSnapshot({ |
| 157 | threadId: input.threadId, | 162 | threadId: input.threadId, |
| 158 | tabId, | 163 | tabId, |
| 159 | url: yield* normalizeUrl(input.url), | 164 | url: yield* normalizeUrl(input.url), |
| 160 | title: "", | 165 | title: "", |
| 166 | updatedAt, | ||
| 161 | }) | 167 | }) |
| 162 | : buildIdleSnapshot({ threadId: input.threadId, tabId }); | 168 | : buildIdleSnapshot({ threadId: input.threadId, tabId, updatedAt }); |
| 163 | yield* SynchronizedRef.update(stateRef, (state) => { | 169 | yield* SynchronizedRef.update(stateRef, (state) => { |
| 164 | const sessions = new Map(state.sessions); | 170 | const sessions = new Map(state.sessions); |
| 165 | sessions.set(compositeKey(input.threadId, tabId), { | 171 | sessions.set(compositeKey(input.threadId, tabId), { |
| ... | @@ -183,7 +189,8 @@ export const makePreviewManager = Effect.gen(function* () { | ... | @@ -183,7 +189,8 @@ export const makePreviewManager = Effect.gen(function* () { |
| 183 | Effect.gen(function* () { | 189 | Effect.gen(function* () { |
| 184 | const url = yield* normalizeUrl(input.url); | 190 | const url = yield* normalizeUrl(input.url); |
| 185 | return yield* mutateExistingSession(input.threadId, input.tabId, (session) => | 191 | return yield* mutateExistingSession(input.threadId, input.tabId, (session) => |
| 186 | Effect.sync(() => { | 192 | Effect.gen(function* () { |
| 193 | const updatedAt = yield* currentIsoTimestamp; | ||
| 187 | const previousTitle = | 194 | const previousTitle = |
| 188 | session.snapshot.navStatus._tag === "Idle" ? "" : session.snapshot.navStatus.title; | 195 | session.snapshot.navStatus._tag === "Idle" ? "" : session.snapshot.navStatus.title; |
| 189 | const resolvedTitle = input.resolvedTitle ?? previousTitle; | 196 | const resolvedTitle = input.resolvedTitle ?? previousTitle; |
| ... | @@ -193,7 +200,7 @@ export const makePreviewManager = Effect.gen(function* () { | ... | @@ -193,7 +200,7 @@ export const makePreviewManager = Effect.gen(function* () { |
| 193 | navStatus: { _tag: "Success", url, title: resolvedTitle }, | 200 | navStatus: { _tag: "Success", url, title: resolvedTitle }, |
| 194 | canGoBack: session.snapshot.canGoBack, | 201 | canGoBack: session.snapshot.canGoBack, |
| 195 | canGoForward: session.snapshot.canGoForward, | 202 | canGoForward: session.snapshot.canGoForward, |
| 196 | updatedAt: new Date().toISOString(), | 203 | updatedAt, |
| 197 | }; | 204 | }; |
| 198 | return { | 205 | return { |
| 199 | next: { ...session, snapshot }, | 206 | next: { ...session, snapshot }, |
| ... | @@ -212,14 +219,15 @@ export const makePreviewManager = Effect.gen(function* () { | ... | @@ -212,14 +219,15 @@ export const makePreviewManager = Effect.gen(function* () { |
| 212 | 219 | ||
| 213 | const reportStatus: PreviewManagerShape["reportStatus"] = (input) => | 220 | const reportStatus: PreviewManagerShape["reportStatus"] = (input) => |
| 214 | mutateExistingSession(input.threadId, input.tabId, (session) => | 221 | mutateExistingSession(input.threadId, input.tabId, (session) => |
| 215 | Effect.sync(() => { | 222 | Effect.gen(function* () { |
| 223 | const updatedAt = yield* currentIsoTimestamp; | ||
| 216 | const snapshot: PreviewSessionSnapshot = { | 224 | const snapshot: PreviewSessionSnapshot = { |
| 217 | threadId: session.threadId, | 225 | threadId: session.threadId, |
| 218 | tabId: session.tabId, | 226 | tabId: session.tabId, |
| 219 | navStatus: input.navStatus, | 227 | navStatus: input.navStatus, |
| 220 | canGoBack: input.canGoBack, | 228 | canGoBack: input.canGoBack, |
| 221 | canGoForward: input.canGoForward, | 229 | canGoForward: input.canGoForward, |
| 222 | updatedAt: new Date().toISOString(), | 230 | updatedAt, |
| 223 | }; | 231 | }; |
| 224 | const emit: PreviewEvent = | 232 | const emit: PreviewEvent = |
| 225 | input.navStatus._tag === "LoadFailed" | 233 | input.navStatus._tag === "LoadFailed" |
| ... | @@ -256,8 +264,9 @@ export const makePreviewManager = Effect.gen(function* () { | ... | @@ -256,8 +264,9 @@ export const makePreviewManager = Effect.gen(function* () { |
| 256 | ); | 264 | ); |
| 257 | 265 | ||
| 258 | const close: PreviewManagerShape["close"] = (input) => | 266 | const close: PreviewManagerShape["close"] = (input) => |
| 259 | Effect.flatMap( | 267 | Effect.gen(function* () { |
| 260 | SynchronizedRef.modify(stateRef, (state) => { | 268 | const createdAt = yield* currentIsoTimestamp; |
| 269 | const events = yield* SynchronizedRef.modify(stateRef, (state) => { | ||
| 261 | const eventsToEmit: PreviewEvent[] = []; | 270 | const eventsToEmit: PreviewEvent[] = []; |
| 262 | const sessions = new Map(state.sessions); | 271 | const sessions = new Map(state.sessions); |
| 263 | const targets = input.tabId | 272 | const targets = input.tabId |
| ... | @@ -271,21 +280,20 @@ export const makePreviewManager = Effect.gen(function* () { | ... | @@ -271,21 +280,20 @@ export const makePreviewManager = Effect.gen(function* () { |
| 271 | type: "closed", | 280 | type: "closed", |
| 272 | threadId: target.threadId, | 281 | threadId: target.threadId, |
| 273 | tabId: target.tabId, | 282 | tabId: target.tabId, |
| 274 | createdAt: new Date().toISOString(), | 283 | createdAt, |
| 275 | }); | 284 | }); |
| 276 | } | 285 | } |
| 277 | if (eventsToEmit.length === 0) { | 286 | if (eventsToEmit.length === 0) { |
| 278 | return [eventsToEmit, state] as const; | 287 | return [eventsToEmit, state] as const; |
| 279 | } | 288 | } |
| 280 | return [eventsToEmit, { sessions }] as const; | 289 | return [eventsToEmit, { sessions }] as const; |
| 281 | }), | 290 | }); |
| 282 | (events) => | 291 | if (events.length > 0) { |
| 283 | events.length === 0 | 292 | yield* Effect.forEach(events, (event) => PubSub.publish(eventsPubSub, event), { |
| 284 | ? Effect.void | 293 | discard: true, |
| 285 | : Effect.forEach(events, (event) => PubSub.publish(eventsPubSub, event), { | 294 | }); |
| 286 | discard: true, | 295 | } |
| 287 | }), | 296 | }); |
| 288 | ); | ||
| 289 | 297 | ||
| 290 | const list: PreviewManagerShape["list"] = (input) => | 298 | const list: PreviewManagerShape["list"] = (input) => |
| 291 | SynchronizedRef.get(stateRef).pipe( | 299 | SynchronizedRef.get(stateRef).pipe( |
apps/server/src/preview/Layers/PortScanner.test.ts+46-42| ... | @@ -1,12 +1,18 @@ | ... | @@ -1,12 +1,18 @@ |
| 1 | import * as net from "node:net"; | 1 | import * as net from "node:net"; |
| 2 | 2 | ||
| 3 | import { Effect } from "effect"; | 3 | import { it as effectIt } from "@effect/vitest"; |
| 4 | import { afterEach, beforeEach, describe, expect, it } from "vitest"; | 4 | import { Effect, Layer } from "effect"; |
| 5 | import { afterEach, beforeEach, describe, expect, it } from "vite-plus/test"; | ||
| 5 | 6 | ||
| 6 | import { PreviewPortScanner } from "../Services/PortScanner.ts"; | 7 | import { COMMON_DEV_PORTS, PreviewPortScanner } from "../Services/PortScanner.ts"; |
| 8 | import { ProcessRunner } from "../../processRunner.ts"; | ||
| 7 | import { __testing, PreviewPortScannerLive } from "./PortScanner.ts"; | 9 | import { __testing, PreviewPortScannerLive } from "./PortScanner.ts"; |
| 8 | 10 | ||
| 9 | const { parseLsofOutput, parsePortFromLsofName, serversEqual } = __testing; | 11 | const { parseLsofOutput, parsePortFromLsofName, serversEqual } = __testing; |
| 12 | const TestProcessRunner = Layer.succeed(ProcessRunner, { | ||
| 13 | run: () => Effect.die("ProcessRunner should not be used by Windows TCP probe tests"), | ||
| 14 | }); | ||
| 15 | const TestPreviewPortScannerLive = PreviewPortScannerLive.pipe(Layer.provide(TestProcessRunner)); | ||
| 10 | 16 | ||
| 11 | describe("parsePortFromLsofName", () => { | 17 | describe("parsePortFromLsofName", () => { |
| 12 | it("parses *:port", () => { | 18 | it("parses *:port", () => { |
| ... | @@ -121,16 +127,25 @@ describe("serversEqual", () => { | ... | @@ -121,16 +127,25 @@ describe("serversEqual", () => { |
| 121 | describe("PreviewPortScanner integration (TCP probe path)", () => { | 127 | describe("PreviewPortScanner integration (TCP probe path)", () => { |
| 122 | let originalPlatform: NodeJS.Platform; | 128 | let originalPlatform: NodeJS.Platform; |
| 123 | let server: net.Server; | 129 | let server: net.Server; |
| 130 | let port: number; | ||
| 124 | 131 | ||
| 125 | beforeEach(async () => { | 132 | beforeEach(async () => { |
| 126 | originalPlatform = process.platform; | 133 | originalPlatform = process.platform; |
| 127 | Object.defineProperty(process, "platform", { value: "win32", configurable: true }); | 134 | Object.defineProperty(process, "platform", { value: "win32", configurable: true }); |
| 128 | // 3001 is in COMMON_DEV_PORTS so the TCP-probe pass will check it. | 135 | for (const candidate of COMMON_DEV_PORTS) { |
| 129 | server = net.createServer(); | 136 | const candidateServer = net.createServer(); |
| 130 | await new Promise<void>((resolve, reject) => { | 137 | const listening = await new Promise<boolean>((resolve) => { |
| 131 | server.once("error", reject); | 138 | candidateServer.once("error", () => resolve(false)); |
| 132 | server.listen(3001, "127.0.0.1", () => resolve()); | 139 | candidateServer.listen(candidate, "127.0.0.1", () => resolve(true)); |
| 133 | }); | 140 | }); |
| 141 | if (listening) { | ||
| 142 | server = candidateServer; | ||
| 143 | port = candidate; | ||
| 144 | return; | ||
| 145 | } | ||
| 146 | candidateServer.close(); | ||
| 147 | } | ||
| 148 | throw new Error("No common development port was available for the preview scanner test"); | ||
| 134 | }); | 149 | }); |
| 135 | 150 | ||
| 136 | afterEach(async () => { | 151 | afterEach(async () => { |
| ... | @@ -141,40 +156,29 @@ describe("PreviewPortScanner integration (TCP probe path)", () => { | ... | @@ -141,40 +156,29 @@ describe("PreviewPortScanner integration (TCP probe path)", () => { |
| 141 | await new Promise<void>((resolve) => server.close(() => resolve())); | 156 | await new Promise<void>((resolve) => server.close(() => resolve())); |
| 142 | }); | 157 | }); |
| 143 | 158 | ||
| 144 | it("scan() returns a server we just opened on a curated dev port", async () => { | 159 | effectIt.effect("scan() returns a server we just opened on a curated dev port", () => |
| 145 | const result = await Effect.runPromise( | 160 | Effect.gen(function* () { |
| 146 | Effect.scoped( | 161 | const scanner = yield* PreviewPortScanner; |
| 147 | Effect.gen(function* () { | 162 | const result = yield* scanner.scan(); |
| 148 | const scanner = yield* PreviewPortScanner; | 163 | const found = result.find((server) => server.port === port); |
| 149 | return yield* scanner.scan(); | 164 | expect(found).toBeDefined(); |
| 150 | }).pipe(Effect.provide(PreviewPortScannerLive)), | 165 | expect(found?.host).toBe("localhost"); |
| 151 | ), | 166 | }).pipe(Effect.provide(TestPreviewPortScannerLive)), |
| 152 | ); | 167 | ); |
| 153 | const found = result.find((s) => s.port === 3001); | ||
| 154 | expect(found).toBeDefined(); | ||
| 155 | expect(found?.host).toBe("localhost"); | ||
| 156 | }); | ||
| 157 | 168 | ||
| 158 | it("retain() drives an immediate broadcast to subscribers", async () => { | 169 | effectIt.effect("retain() drives an immediate broadcast to subscribers", () => { |
| 159 | const received: number[] = []; | 170 | const received: number[] = []; |
| 160 | await Effect.runPromise( | 171 | return Effect.gen(function* () { |
| 161 | Effect.scoped( | 172 | const scanner = yield* PreviewPortScanner; |
| 162 | Effect.gen(function* () { | 173 | const unsubscribe = yield* scanner.subscribe((servers) => |
| 163 | const scanner = yield* PreviewPortScanner; | 174 | Effect.sync(() => { |
| 164 | const unsubscribe = yield* scanner.subscribe((servers) => | 175 | for (const server of servers) received.push(server.port); |
| 165 | Effect.sync(() => { | 176 | }), |
| 166 | for (const server of servers) received.push(server.port); | 177 | ); |
| 167 | }), | 178 | const release = yield* scanner.retain(); |
| 168 | ); | 179 | unsubscribe(); |
| 169 | const release = yield* scanner.retain(); | 180 | release(); |
| 170 | // The retain() pollTick is fire-and-forget within Effect, so wait | 181 | expect(received).toContain(port); |
| 171 | // a frame for the broadcast to land. | 182 | }).pipe(Effect.provide(TestPreviewPortScannerLive)); |
| 172 | yield* Effect.sleep("100 millis"); | ||
| 173 | unsubscribe(); | ||
| 174 | release(); | ||
| 175 | }).pipe(Effect.provide(PreviewPortScannerLive)), | ||
| 176 | ), | ||
| 177 | ); | ||
| 178 | expect(received).toContain(3001); | ||
| 179 | }); | 183 | }); |
| 180 | }); | 184 | }); |
apps/server/src/preview/Layers/PortScanner.ts+19-16| ... | @@ -17,7 +17,7 @@ import type { DiscoveredLocalServer } from "@t3tools/contracts"; | ... | @@ -17,7 +17,7 @@ import type { DiscoveredLocalServer } from "@t3tools/contracts"; |
| 17 | import { LSOF_LOCAL_HOST_TOKENS } from "@t3tools/shared/preview"; | 17 | import { LSOF_LOCAL_HOST_TOKENS } from "@t3tools/shared/preview"; |
| 18 | import { Cause, Data, Duration, Effect, Layer, Ref, Schedule } from "effect"; | 18 | import { Cause, Data, Duration, Effect, Layer, Ref, Schedule } from "effect"; |
| 19 | 19 | ||
| 20 | import { runProcess } from "../../processRunner.ts"; | 20 | import { ProcessRunner, type ProcessRunnerShape } from "../../processRunner.ts"; |
| 21 | import { | 21 | import { |
| 22 | COMMON_DEV_PORTS, | 22 | COMMON_DEV_PORTS, |
| 23 | PreviewPortScanner, | 23 | PreviewPortScanner, |
| ... | @@ -91,20 +91,22 @@ const parsePortFromLsofName = (name: string): number | null => { | ... | @@ -91,20 +91,22 @@ const parsePortFromLsofName = (name: string): number | null => { |
| 91 | return port; | 91 | return port; |
| 92 | }; | 92 | }; |
| 93 | 93 | ||
| 94 | const probeLsof = (): Effect.Effect<ReadonlyArray<DiscoveredLocalServer> | null> => | 94 | const probeLsof = ( |
| 95 | Effect.tryPromise({ | 95 | processRunner: ProcessRunnerShape, |
| 96 | try: () => | 96 | ): Effect.Effect<ReadonlyArray<DiscoveredLocalServer> | null> => |
| 97 | runProcess("lsof", ["-iTCP", "-sTCP:LISTEN", "-P", "-n", "-F", "pcn"], { | 97 | processRunner |
| 98 | timeoutMs: LSOF_TIMEOUT_MS, | 98 | .run({ |
| 99 | allowNonZeroExit: true, | 99 | command: "lsof", |
| 100 | maxBufferBytes: 1024 * 1024, | 100 | args: ["-iTCP", "-sTCP:LISTEN", "-P", "-n", "-F", "pcn"], |
| 101 | outputMode: "truncate", | 101 | timeout: Duration.millis(LSOF_TIMEOUT_MS), |
| 102 | }), | 102 | maxOutputBytes: 1024 * 1024, |
| 103 | catch: (cause) => new LsofProbeError({ cause }), | 103 | outputMode: "truncate", |
| 104 | }).pipe( | 104 | }) |
| 105 | Effect.map((result) => parseLsofOutput(result.stdout)), | 105 | .pipe( |
| 106 | Effect.catch(() => Effect.succeed(null)), | 106 | Effect.mapError((cause) => new LsofProbeError({ cause })), |
| 107 | ); | 107 | Effect.map((result) => parseLsofOutput(result.stdout)), |
| 108 | Effect.orElseSucceed(() => null), | ||
| 109 | ); | ||
| 108 | 110 | ||
| 109 | const probeTcpPort = (port: number): Promise<boolean> => | 111 | const probeTcpPort = (port: number): Promise<boolean> => |
| 110 | new Promise((resolve) => { | 112 | new Promise((resolve) => { |
| ... | @@ -162,6 +164,7 @@ const serversEqual = ( | ... | @@ -162,6 +164,7 @@ const serversEqual = ( |
| 162 | }; | 164 | }; |
| 163 | 165 | ||
| 164 | export const makePreviewPortScanner = Effect.gen(function* () { | 166 | export const makePreviewPortScanner = Effect.gen(function* () { |
| 167 | const processRunner = yield* ProcessRunner; | ||
| 165 | const stateRef = yield* Ref.make<ScannerState>({ | 168 | const stateRef = yield* Ref.make<ScannerState>({ |
| 166 | lastSnapshot: [], | 169 | lastSnapshot: [], |
| 167 | }); | 170 | }); |
| ... | @@ -176,7 +179,7 @@ export const makePreviewPortScanner = Effect.gen(function* () { | ... | @@ -176,7 +179,7 @@ export const makePreviewPortScanner = Effect.gen(function* () { |
| 176 | if (process.platform === "win32") { | 179 | if (process.platform === "win32") { |
| 177 | return yield* probeCommonPorts(); | 180 | return yield* probeCommonPorts(); |
| 178 | } | 181 | } |
| 179 | const lsof = yield* probeLsof(); | 182 | const lsof = yield* probeLsof(processRunner); |
| 180 | if (lsof !== null) return lsof; | 183 | if (lsof !== null) return lsof; |
| 181 | return yield* probeCommonPorts(); | 184 | return yield* probeCommonPorts(); |
| 182 | }); | 185 | }); |
apps/server/src/preview/Services/PortScanner.ts+1-1| ... | @@ -28,7 +28,7 @@ export interface PreviewPortScannerShape { | ... | @@ -28,7 +28,7 @@ export interface PreviewPortScannerShape { |
| 28 | export class PreviewPortScanner extends Context.Service< | 28 | export class PreviewPortScanner extends Context.Service< |
| 29 | PreviewPortScanner, | 29 | PreviewPortScanner, |
| 30 | PreviewPortScannerShape | 30 | PreviewPortScannerShape |
| 31 | >()("t3/preview/Services/PortScanner") {} | 31 | >()("t3/preview/Services/PortScanner/PreviewPortScanner") {} |
| 32 | 32 | ||
| 33 | /** Curated list of common dev-server ports for the Windows TCP-probe fallback. */ | 33 | /** Curated list of common dev-server ports for the Windows TCP-probe fallback. */ |
| 34 | export const COMMON_DEV_PORTS: ReadonlyArray<number> = Object.freeze([ | 34 | export const COMMON_DEV_PORTS: ReadonlyArray<number> = Object.freeze([ |
apps/server/src/server.test.ts+19-19| ... | @@ -668,25 +668,25 @@ const buildAppUnderTest = (options?: { | ... | @@ -668,25 +668,25 @@ const buildAppUnderTest = (options?: { |
| 668 | }), | 668 | }), |
| 669 | ), | 669 | ), |
| 670 | Layer.provide( | 670 | Layer.provide( |
| 671 | Layer.mock(PreviewManager)({ | 671 | Layer.mergeAll( |
| 672 | open: () => Effect.die("PreviewManager not stubbed in this test"), | 672 | Layer.mock(PreviewManager)({ |
| 673 | navigate: () => Effect.die("PreviewManager not stubbed in this test"), | 673 | open: () => Effect.die("PreviewManager not stubbed in this test"), |
| 674 | reportStatus: () => Effect.void, | 674 | navigate: () => Effect.die("PreviewManager not stubbed in this test"), |
| 675 | refresh: () => Effect.void, | 675 | reportStatus: () => Effect.void, |
| 676 | close: () => Effect.void, | 676 | refresh: () => Effect.void, |
| 677 | list: () => Effect.succeed({ sessions: [] }), | 677 | close: () => Effect.void, |
| 678 | events: Stream.empty, | 678 | list: () => Effect.succeed({ sessions: [] }), |
| 679 | subscribeEvents: Effect.flatMap(PubSub.unbounded<PreviewEvent>(), (pubsub) => | 679 | events: Stream.empty, |
| 680 | PubSub.subscribe(pubsub), | 680 | subscribeEvents: Effect.flatMap(PubSub.unbounded<PreviewEvent>(), (pubsub) => |
| 681 | ), | 681 | PubSub.subscribe(pubsub), |
| 682 | }), | 682 | ), |
| 683 | ), | 683 | }), |
| 684 | Layer.provide( | 684 | Layer.mock(PreviewPortScanner)({ |
| 685 | Layer.mock(PreviewPortScanner)({ | 685 | scan: () => Effect.succeed([]), |
| 686 | scan: () => Effect.succeed([]), | 686 | subscribe: () => Effect.succeed(() => {}), |
| 687 | subscribe: () => Effect.succeed(() => {}), | 687 | retain: () => Effect.succeed(() => {}), |
| 688 | retain: () => Effect.succeed(() => {}), | 688 | }), |
| 689 | }), | 689 | ), |
| 690 | ), | 690 | ), |
| 691 | Layer.provide( | 691 | Layer.provide( |
| 692 | Layer.mock(OrchestrationEngineService)({ | 692 | Layer.mock(OrchestrationEngineService)({ |
apps/server/src/server.ts+6-3| ... | @@ -37,6 +37,7 @@ import { ProviderInstanceRegistryHydrationLive } from "./provider/Layers/Provide | ... | @@ -37,6 +37,7 @@ import { ProviderInstanceRegistryHydrationLive } from "./provider/Layers/Provide |
| 37 | import { TerminalManagerLive } from "./terminal/Layers/Manager.ts"; | 37 | import { TerminalManagerLive } from "./terminal/Layers/Manager.ts"; |
| 38 | import { PreviewManagerLive } from "./preview/Layers/Manager.ts"; | 38 | import { PreviewManagerLive } from "./preview/Layers/Manager.ts"; |
| 39 | import { PreviewPortScannerLive } from "./preview/Layers/PortScanner.ts"; | 39 | import { PreviewPortScannerLive } from "./preview/Layers/PortScanner.ts"; |
| 40 | import * as ProcessRunner from "./processRunner.ts"; | ||
| 40 | import * as GitManager from "./git/GitManager.ts"; | 41 | import * as GitManager from "./git/GitManager.ts"; |
| 41 | import { KeybindingsLive } from "./keybindings.ts"; | 42 | import { KeybindingsLive } from "./keybindings.ts"; |
| 42 | import { ServerRuntimeStartup, ServerRuntimeStartupLive } from "./serverRuntimeStartup.ts"; | 43 | import { ServerRuntimeStartup, ServerRuntimeStartupLive } from "./serverRuntimeStartup.ts"; |
| ... | @@ -235,7 +236,10 @@ const CheckpointingLayerLive = Layer.empty.pipe( | ... | @@ -235,7 +236,10 @@ const CheckpointingLayerLive = Layer.empty.pipe( |
| 235 | 236 | ||
| 236 | const TerminalLayerLive = TerminalManagerLive.pipe(Layer.provide(PtyAdapterLive)); | 237 | const TerminalLayerLive = TerminalManagerLive.pipe(Layer.provide(PtyAdapterLive)); |
| 237 | 238 | ||
| 238 | const PreviewLayerLive = Layer.mergeAll(PreviewManagerLive, PreviewPortScannerLive); | 239 | const PreviewLayerLive = Layer.mergeAll( |
| 240 | PreviewManagerLive, | ||
| 241 | PreviewPortScannerLive.pipe(Layer.provide(ProcessRunner.layer)), | ||
| 242 | ); | ||
| 239 | 243 | ||
| 240 | const WorkspaceEntriesLayerLive = WorkspaceEntriesLive.pipe( | 244 | const WorkspaceEntriesLayerLive = WorkspaceEntriesLive.pipe( |
| 241 | Layer.provide(WorkspacePathsLive), | 245 | Layer.provide(WorkspacePathsLive), |
| ... | @@ -278,8 +282,7 @@ const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe( | ... | @@ -278,8 +282,7 @@ const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe( |
| 278 | Layer.provideMerge(GitLayerLive), | 282 | Layer.provideMerge(GitLayerLive), |
| 279 | Layer.provideMerge(VcsLayerLive), | 283 | Layer.provideMerge(VcsLayerLive), |
| 280 | Layer.provideMerge(ProviderRuntimeLayerLive), | 284 | Layer.provideMerge(ProviderRuntimeLayerLive), |
| 281 | Layer.provideMerge(TerminalLayerLive), | 285 | Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)), |
| 282 | Layer.provideMerge(PreviewLayerLive), | ||
| 283 | Layer.provideMerge(PersistenceLayerLive), | 286 | Layer.provideMerge(PersistenceLayerLive), |
| 284 | Layer.provideMerge(KeybindingsLive), | 287 | Layer.provideMerge(KeybindingsLive), |
| 285 | Layer.provideMerge(ProviderRegistryLive), | 288 | Layer.provideMerge(ProviderRegistryLive), |
apps/server/src/ws.ts+5-4| ... | @@ -1391,14 +1391,15 @@ const makeWsRpcLayer = (currentSession: AuthenticatedSession) => | ... | @@ -1391,14 +1391,15 @@ const makeWsRpcLayer = (currentSession: AuthenticatedSession) => |
| 1391 | Effect.gen(function* () { | 1391 | Effect.gen(function* () { |
| 1392 | const release = yield* previewPortScanner.retain(); | 1392 | const release = yield* previewPortScanner.retain(); |
| 1393 | const initial = yield* previewPortScanner.scan(); | 1393 | const initial = yield* previewPortScanner.scan(); |
| 1394 | const initialScannedAt = DateTime.formatIso(yield* DateTime.now); | ||
| 1394 | yield* Queue.offer(queue, { | 1395 | yield* Queue.offer(queue, { |
| 1395 | servers: initial, | 1396 | servers: initial, |
| 1396 | scannedAt: new Date().toISOString(), | 1397 | scannedAt: initialScannedAt, |
| 1397 | }); | 1398 | }); |
| 1398 | const unsubscribe = yield* previewPortScanner.subscribe((servers) => | 1399 | const unsubscribe = yield* previewPortScanner.subscribe((servers) => |
| 1399 | Queue.offer(queue, { | 1400 | Effect.gen(function* () { |
| 1400 | servers, | 1401 | const scannedAt = DateTime.formatIso(yield* DateTime.now); |
| 1401 | scannedAt: new Date().toISOString(), | 1402 | yield* Queue.offer(queue, { servers, scannedAt }); |
| 1402 | }), | 1403 | }), |
| 1403 | ); | 1404 | ); |
| 1404 | return { unsubscribe, release }; | 1405 | return { unsubscribe, release }; |
apps/web/src/components/chat/ChatComposer.tsx+6-8| ... | @@ -17,10 +17,6 @@ import { | ... | @@ -17,10 +17,6 @@ import { |
| 17 | PROVIDER_SEND_TURN_MAX_ATTACHMENTS, | 17 | PROVIDER_SEND_TURN_MAX_ATTACHMENTS, |
| 18 | PROVIDER_SEND_TURN_MAX_IMAGE_BYTES, | 18 | PROVIDER_SEND_TURN_MAX_IMAGE_BYTES, |
| 19 | } from "@t3tools/contracts"; | 19 | } from "@t3tools/contracts"; |
| 20 | import { | ||
| 21 | connectionStatusText, | ||
| 22 | type EnvironmentConnectionPresentation, | ||
| 23 | } from "@t3tools/client-runtime/connection"; | ||
| 24 | import { serializeComposerFileLink } from "@t3tools/shared/composerTrigger"; | 20 | import { serializeComposerFileLink } from "@t3tools/shared/composerTrigger"; |
| 25 | import { createModelSelection, normalizeModelSlug } from "@t3tools/shared/model"; | 21 | import { createModelSelection, normalizeModelSlug } from "@t3tools/shared/model"; |
| 26 | import { | 22 | import { |
| ... | @@ -441,7 +437,7 @@ export interface ChatComposerProps { | ... | @@ -441,7 +437,7 @@ export interface ChatComposerProps { |
| 441 | isPreparingWorktree: boolean; | 437 | isPreparingWorktree: boolean; |
| 442 | environmentUnavailable: { | 438 | environmentUnavailable: { |
| 443 | readonly label: string; | 439 | readonly label: string; |
| 444 | readonly connection: EnvironmentConnectionPresentation; | 440 | readonly connectionState: "connecting" | "disconnected" | "error"; |
| 445 | } | null; | 441 | } | null; |
| 446 | 442 | ||
| 447 | // Pending approvals / inputs | 443 | // Pending approvals / inputs |
| ... | @@ -2354,9 +2350,11 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -2354,9 +2350,11 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 2354 | : showPlanFollowUpPrompt && activeProposedPlan | 2350 | : showPlanFollowUpPrompt && activeProposedPlan |
| 2355 | ? "Add feedback to refine the plan, or leave this blank to implement it" | 2351 | ? "Add feedback to refine the plan, or leave this blank to implement it" |
| 2356 | : environmentUnavailable | 2352 | : environmentUnavailable |
| 2357 | ? `${environmentUnavailable.label}: ${connectionStatusText( | 2353 | ? `${environmentUnavailable.label} is ${ |
| 2358 | environmentUnavailable.connection, | 2354 | environmentUnavailable.connectionState === "connecting" |
| 2359 | )}` | 2355 | ? "connecting" |
| 2356 | : "disconnected" | ||
| 2357 | }` | ||
| 2360 | : phase === "disconnected" | 2358 | : phase === "disconnected" |
| 2361 | ? "Ask for follow-up changes or attach images" | 2359 | ? "Ask for follow-up changes or attach images" |
| 2362 | : "Ask anything, @tag files/folders, $use skills, or / for commands" | 2360 | : "Ask anything, @tag files/folders, $use skills, or / for commands" |
apps/web/src/components/chat/ChatHeader.tsx-1| ... | @@ -10,7 +10,6 @@ import { memo } from "react"; | ... | @@ -10,7 +10,6 @@ import { memo } from "react"; |
| 10 | import GitActionsControl from "../GitActionsControl"; | 10 | import GitActionsControl from "../GitActionsControl"; |
| 11 | import { type DraftId } from "~/composerDraftStore"; | 11 | import { type DraftId } from "~/composerDraftStore"; |
| 12 | import { DiffIcon, Globe, TerminalSquareIcon } from "lucide-react"; | 12 | import { DiffIcon, Globe, TerminalSquareIcon } from "lucide-react"; |
| 13 | import { Badge } from "../ui/badge"; | ||
| 14 | import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; | 13 | import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; |
| 15 | import ProjectScriptsControl, { type NewProjectScriptInput } from "../ProjectScriptsControl"; | 14 | import ProjectScriptsControl, { type NewProjectScriptInput } from "../ProjectScriptsControl"; |
| 16 | import { Toggle } from "../ui/toggle"; | 15 | import { Toggle } from "../ui/toggle"; |
apps/web/src/components/chat/MessagesTimeline.tsx+10-4| ... | @@ -470,9 +470,9 @@ function UserTimelineRow({ row }: { row: Extract<TimelineRow, { kind: "message" | ... | @@ -470,9 +470,9 @@ function UserTimelineRow({ row }: { row: Extract<TimelineRow, { kind: "message" |
| 470 | )} | 470 | )} |
| 471 | {elementContextState.contexts.length > 0 ? ( | 471 | {elementContextState.contexts.length > 0 ? ( |
| 472 | <div className="mb-2 flex flex-wrap gap-1.5"> | 472 | <div className="mb-2 flex flex-wrap gap-1.5"> |
| 473 | {elementContextState.contexts.map((context, index) => ( | 473 | {elementContextState.contexts.map((context) => ( |
| 474 | <UserMessageElementContextChip | 474 | <UserMessageElementContextChip |
| 475 | key={`${context.header}:${index}`} | 475 | key={`${context.header}:${context.body}`} |
| 476 | context={context} | 476 | context={context} |
| 477 | /> | 477 | /> |
| 478 | ))} | 478 | ))} |
| ... | @@ -579,10 +579,16 @@ function AssistantTimelineRow({ row }: { row: Extract<TimelineRow, { kind: "mess | ... | @@ -579,10 +579,16 @@ function AssistantTimelineRow({ row }: { row: Extract<TimelineRow, { kind: "mess |
| 579 | <TooltipTrigger | 579 | <TooltipTrigger |
| 580 | render={<p className="text-muted-foreground text-xs tabular-nums" />} | 580 | render={<p className="text-muted-foreground text-xs tabular-nums" />} |
| 581 | > | 581 | > |
| 582 | {formatShortTimestamp(row.message.updatedAt, ctx.timestampFormat)} | 582 | {formatShortTimestamp( |
| 583 | row.message.completedAt ?? row.message.createdAt, | ||
| 584 | ctx.timestampFormat, | ||
| 585 | )} | ||
| 583 | </TooltipTrigger> | 586 | </TooltipTrigger> |
| 584 | <TooltipPopup> | 587 | <TooltipPopup> |
| 585 | {formatChatTimestampTooltip(row.message.updatedAt, ctx.timestampFormat)} | 588 | {formatChatTimestampTooltip( |
| 589 | row.message.completedAt ?? row.message.createdAt, | ||
| 590 | ctx.timestampFormat, | ||
| 591 | )} | ||
| 586 | </TooltipPopup> | 592 | </TooltipPopup> |
| 587 | </Tooltip> | 593 | </Tooltip> |
| 588 | )} | 594 | )} |
apps/web/src/components/preview/useDiscoveredLocalServers.test.ts+1-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | import type { DiscoveredLocalServer } from "@t3tools/contracts"; | 1 | import type { DiscoveredLocalServer } from "@t3tools/contracts"; |
| 2 | import { describe, expect, it } from "vitest"; | 2 | import { describe, expect, it } from "vite-plus/test"; |
| 3 | 3 | ||
| 4 | import { mergeServers, type PreviewableServer } from "./useDiscoveredLocalServers"; | 4 | import { mergeServers, type PreviewableServer } from "./useDiscoveredLocalServers"; |
| 5 | 5 |
apps/web/src/environments/runtime/service.threadSubscriptions.test.ts+10| ... | @@ -104,6 +104,16 @@ vi.mock("@t3tools/client-runtime", async (importOriginal) => { | ... | @@ -104,6 +104,16 @@ vi.mock("@t3tools/client-runtime", async (importOriginal) => { |
| 104 | onEvent: vi.fn(() => () => undefined), | 104 | onEvent: vi.fn(() => () => undefined), |
| 105 | onMetadata: vi.fn(() => () => undefined), | 105 | onMetadata: vi.fn(() => () => undefined), |
| 106 | }, | 106 | }, |
| 107 | preview: { | ||
| 108 | open: vi.fn(), | ||
| 109 | navigate: vi.fn(), | ||
| 110 | refresh: vi.fn(), | ||
| 111 | close: vi.fn(), | ||
| 112 | list: vi.fn(), | ||
| 113 | reportStatus: vi.fn(), | ||
| 114 | onEvent: vi.fn(() => () => undefined), | ||
| 115 | subscribePorts: vi.fn(() => () => undefined), | ||
| 116 | }, | ||
| 107 | projects: { | 117 | projects: { |
| 108 | searchEntries: vi.fn(), | 118 | searchEntries: vi.fn(), |
| 109 | writeFile: vi.fn(), | 119 | writeFile: vi.fn(), |
apps/web/src/lib/elementContext.test.ts+1-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | import type { PickedElementPayload } from "@t3tools/contracts"; | 1 | import type { PickedElementPayload } from "@t3tools/contracts"; |
| 2 | import { describe, expect, it } from "vitest"; | 2 | import { describe, expect, it } from "vite-plus/test"; |
| 3 | 3 | ||
| 4 | import { | 4 | import { |
| 5 | appendElementContextsToPrompt, | 5 | appendElementContextsToPrompt, |
apps/web/src/lib/elementContext.ts+3-4| ... | @@ -198,12 +198,11 @@ export function appendElementContextsToPrompt( | ... | @@ -198,12 +198,11 @@ export function appendElementContextsToPrompt( |
| 198 | } | 198 | } |
| 199 | 199 | ||
| 200 | const ELEMENT_CONTEXT_ID_PREFIX = "el_"; | 200 | const ELEMENT_CONTEXT_ID_PREFIX = "el_"; |
| 201 | let nextElementContextSequence = 0; | ||
| 201 | 202 | ||
| 202 | export function newElementContextId(): string { | 203 | export function newElementContextId(): string { |
| 203 | if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") { | 204 | nextElementContextSequence += 1; |
| 204 | return `${ELEMENT_CONTEXT_ID_PREFIX}${crypto.randomUUID()}`; | 205 | return `${ELEMENT_CONTEXT_ID_PREFIX}${nextElementContextSequence.toString(36)}`; |
| 205 | } | ||
| 206 | return `${ELEMENT_CONTEXT_ID_PREFIX}${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`; | ||
| 207 | } | 206 | } |
| 208 | 207 | ||
| 209 | /** | 208 | /** |
apps/web/src/previewStateStore.test.ts+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | import { scopeThreadRef } from "@t3tools/client-runtime"; | 1 | import { scopeThreadRef } from "@t3tools/client-runtime"; |
| 2 | import { type EnvironmentId, type PreviewSessionSnapshot, ThreadId } from "@t3tools/contracts"; | 2 | import { type EnvironmentId, type PreviewSessionSnapshot, ThreadId } from "@t3tools/contracts"; |
| 3 | import { beforeEach, describe, expect, it } from "vitest"; | 3 | import { beforeEach, describe, expect, it } from "vite-plus/test"; |
| 4 | 4 | ||
| 5 | import { __testing, selectThreadPreviewState, usePreviewStateStore } from "./previewStateStore"; | 5 | import { __testing, selectThreadPreviewState, usePreviewStateStore } from "./previewStateStore"; |
| 6 | 6 |
apps/web/src/rightPanelStore.test.ts+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | import { scopeThreadRef } from "@t3tools/client-runtime"; | 1 | import { scopeThreadRef } from "@t3tools/client-runtime"; |
| 2 | import { type EnvironmentId, ThreadId } from "@t3tools/contracts"; | 2 | import { type EnvironmentId, ThreadId } from "@t3tools/contracts"; |
| 3 | import { beforeEach, describe, expect, it } from "vitest"; | 3 | import { beforeEach, describe, expect, it } from "vite-plus/test"; |
| 4 | 4 | ||
| 5 | import { | 5 | import { |
| 6 | selectActiveRightPanel, | 6 | selectActiveRightPanel, |
packages/contracts/src/preview.test.ts+1-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | import { Schema } from "effect"; | 1 | import { Schema } from "effect"; |
| 2 | import { describe, expect, it } from "vitest"; | 2 | import { describe, expect, it } from "vite-plus/test"; |
| 3 | 3 | ||
| 4 | import { | 4 | import { |
| 5 | DiscoveredLocalServer, | 5 | DiscoveredLocalServer, |
packages/contracts/src/rpc.ts+8-5| ... | @@ -480,38 +480,40 @@ export const WsTerminalCloseRpc = Rpc.make(WS_METHODS.terminalClose, { | ... | @@ -480,38 +480,40 @@ export const WsTerminalCloseRpc = Rpc.make(WS_METHODS.terminalClose, { |
| 480 | export const WsPreviewOpenRpc = Rpc.make(WS_METHODS.previewOpen, { | 480 | export const WsPreviewOpenRpc = Rpc.make(WS_METHODS.previewOpen, { |
| 481 | payload: PreviewOpenInput, | 481 | payload: PreviewOpenInput, |
| 482 | success: PreviewSessionSnapshot, | 482 | success: PreviewSessionSnapshot, |
| 483 | error: PreviewError, | 483 | error: Schema.Union([PreviewError, EnvironmentAuthorizationError]), |
| 484 | }); | 484 | }); |
| 485 | 485 | ||
| 486 | export const WsPreviewNavigateRpc = Rpc.make(WS_METHODS.previewNavigate, { | 486 | export const WsPreviewNavigateRpc = Rpc.make(WS_METHODS.previewNavigate, { |
| 487 | payload: PreviewNavigateInput, | 487 | payload: PreviewNavigateInput, |
| 488 | success: PreviewSessionSnapshot, | 488 | success: PreviewSessionSnapshot, |
| 489 | error: PreviewError, | 489 | error: Schema.Union([PreviewError, EnvironmentAuthorizationError]), |
| 490 | }); | 490 | }); |
| 491 | 491 | ||
| 492 | export const WsPreviewRefreshRpc = Rpc.make(WS_METHODS.previewRefresh, { | 492 | export const WsPreviewRefreshRpc = Rpc.make(WS_METHODS.previewRefresh, { |
| 493 | payload: PreviewRefreshInput, | 493 | payload: PreviewRefreshInput, |
| 494 | error: PreviewError, | 494 | error: Schema.Union([PreviewError, EnvironmentAuthorizationError]), |
| 495 | }); | 495 | }); |
| 496 | 496 | ||
| 497 | export const WsPreviewCloseRpc = Rpc.make(WS_METHODS.previewClose, { | 497 | export const WsPreviewCloseRpc = Rpc.make(WS_METHODS.previewClose, { |
| 498 | payload: PreviewCloseInput, | 498 | payload: PreviewCloseInput, |
| 499 | error: PreviewError, | 499 | error: Schema.Union([PreviewError, EnvironmentAuthorizationError]), |
| 500 | }); | 500 | }); |
| 501 | 501 | ||
| 502 | export const WsPreviewListRpc = Rpc.make(WS_METHODS.previewList, { | 502 | export const WsPreviewListRpc = Rpc.make(WS_METHODS.previewList, { |
| 503 | payload: PreviewListInput, | 503 | payload: PreviewListInput, |
| 504 | success: PreviewListResult, | 504 | success: PreviewListResult, |
| 505 | error: EnvironmentAuthorizationError, | ||
| 505 | }); | 506 | }); |
| 506 | 507 | ||
| 507 | export const WsPreviewReportStatusRpc = Rpc.make(WS_METHODS.previewReportStatus, { | 508 | export const WsPreviewReportStatusRpc = Rpc.make(WS_METHODS.previewReportStatus, { |
| 508 | payload: PreviewReportStatusInput, | 509 | payload: PreviewReportStatusInput, |
| 509 | error: PreviewError, | 510 | error: Schema.Union([PreviewError, EnvironmentAuthorizationError]), |
| 510 | }); | 511 | }); |
| 511 | 512 | ||
| 512 | export const WsSubscribePreviewEventsRpc = Rpc.make(WS_METHODS.subscribePreviewEvents, { | 513 | export const WsSubscribePreviewEventsRpc = Rpc.make(WS_METHODS.subscribePreviewEvents, { |
| 513 | payload: Schema.Struct({}), | 514 | payload: Schema.Struct({}), |
| 514 | success: PreviewEvent, | 515 | success: PreviewEvent, |
| 516 | error: EnvironmentAuthorizationError, | ||
| 515 | stream: true, | 517 | stream: true, |
| 516 | }); | 518 | }); |
| 517 | 519 | ||
| ... | @@ -520,6 +522,7 @@ export const WsSubscribeDiscoveredLocalServersRpc = Rpc.make( | ... | @@ -520,6 +522,7 @@ export const WsSubscribeDiscoveredLocalServersRpc = Rpc.make( |
| 520 | { | 522 | { |
| 521 | payload: Schema.Struct({}), | 523 | payload: Schema.Struct({}), |
| 522 | success: DiscoveredLocalServerList, | 524 | success: DiscoveredLocalServerList, |
| 525 | error: EnvironmentAuthorizationError, | ||
| 523 | stream: true, | 526 | stream: true, |
| 524 | }, | 527 | }, |
| 525 | ); | 528 | ); |
packages/shared/src/preview.test.ts+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | import { describe, expect, it } from "vitest"; | 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | 2 | ||
| 3 | import { | 3 | import { |
| 4 | isLoopbackHost, | 4 | isLoopbackHost, |
packages/shared/src/preview.ts+3-4| ... | @@ -5,16 +5,15 @@ | ... | @@ -5,16 +5,15 @@ |
| 5 | */ | 5 | */ |
| 6 | 6 | ||
| 7 | const TAB_ID_PREFIX = "tab_"; | 7 | const TAB_ID_PREFIX = "tab_"; |
| 8 | let nextPreviewTabSequence = 0; | ||
| 8 | 9 | ||
| 9 | /** | 10 | /** |
| 10 | * Generate a fresh preview tab id. Lives in shared (not contracts) because | 11 | * Generate a fresh preview tab id. Lives in shared (not contracts) because |
| 11 | * the contracts package is schema-only — runtime helpers belong here. | 12 | * the contracts package is schema-only — runtime helpers belong here. |
| 12 | */ | 13 | */ |
| 13 | export function newPreviewTabId(): string { | 14 | export function newPreviewTabId(): string { |
| 14 | if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") { | 15 | nextPreviewTabSequence += 1; |
| 15 | return `${TAB_ID_PREFIX}${crypto.randomUUID()}`; | 16 | return `${TAB_ID_PREFIX}${nextPreviewTabSequence.toString(36)}`; |
| 16 | } | ||
| 17 | return `${TAB_ID_PREFIX}${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`; | ||
| 18 | } | 17 | } |
| 19 | 18 | ||
| 20 | const LOOPBACK_HOSTS: ReadonlySet<string> = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1"]); | 19 | const LOOPBACK_HOSTS: ReadonlySet<string> = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1"]); |
pnpm-lock.yaml+161| ... | @@ -128,6 +128,9 @@ importers: | ... | @@ -128,6 +128,9 @@ importers: |
| 128 | electron-updater: | 128 | electron-updater: |
| 129 | specifier: ^6.6.2 | 129 | specifier: ^6.6.2 |
| 130 | version: 6.8.3 | 130 | version: 6.8.3 |
| 131 | react-grab: | ||
| 132 | specifier: ^0.1.32 | ||
| 133 | version: 0.1.44(react@19.2.6) | ||
| 131 | devDependencies: | 134 | devDependencies: |
| 132 | '@effect/vitest': | 135 | '@effect/vitest': |
| 133 | specifier: 4.0.0-beta.78 | 136 | specifier: 4.0.0-beta.78 |
| ... | @@ -580,6 +583,9 @@ importers: | ... | @@ -580,6 +583,9 @@ importers: |
| 580 | playwright: | 583 | playwright: |
| 581 | specifier: ^1.58.2 | 584 | specifier: ^1.58.2 |
| 582 | version: 1.60.0 | 585 | version: 1.60.0 |
| 586 | react-grab: | ||
| 587 | specifier: ^0.1.32 | ||
| 588 | version: 0.1.44(react@19.2.6) | ||
| 583 | tailwindcss: | 589 | tailwindcss: |
| 584 | specifier: ^4.0.0 | 590 | specifier: ^4.0.0 |
| 585 | version: 4.3.0 | 591 | version: 4.3.0 |
| ... | @@ -2395,6 +2401,9 @@ packages: | ... | @@ -2395,6 +2401,9 @@ packages: |
| 2395 | peerDependencies: | 2401 | peerDependencies: |
| 2396 | hono: ^4 | 2402 | hono: ^4 |
| 2397 | 2403 | ||
| 2404 | '@iarna/toml@2.2.5': | ||
| 2405 | resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} | ||
| 2406 | |||
| 2398 | '@img/colour@1.1.0': | 2407 | '@img/colour@1.1.0': |
| 2399 | resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} | 2408 | resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} |
| 2400 | engines: {node: '>=18'} | 2409 | engines: {node: '>=18'} |
| ... | @@ -3432,6 +3441,10 @@ packages: | ... | @@ -3432,6 +3441,10 @@ packages: |
| 3432 | '@types/react': | 3441 | '@types/react': |
| 3433 | optional: true | 3442 | optional: true |
| 3434 | 3443 | ||
| 3444 | '@react-grab/cli@0.1.44': | ||
| 3445 | resolution: {integrity: sha512-gMDYY2rw6OWajCcDlXSIgs2LC432YJXSb3Lm5yM187uhRgBYddoEVULi36h+IolX3r7jSb3ew7vn9FfI8NSo0A==} | ||
| 3446 | hasBin: true | ||
| 3447 | |||
| 3435 | '@react-native-masked-view/masked-view@0.3.2': | 3448 | '@react-native-masked-view/masked-view@0.3.2': |
| 3436 | resolution: {integrity: sha512-XwuQoW7/GEgWRMovOQtX3A4PrXhyaZm0lVUiY8qJDvdngjLms9Cpdck6SmGAUNqQwcj2EadHC1HwL0bEyoa/SQ==} | 3449 | resolution: {integrity: sha512-XwuQoW7/GEgWRMovOQtX3A4PrXhyaZm0lVUiY8qJDvdngjLms9Cpdck6SmGAUNqQwcj2EadHC1HwL0bEyoa/SQ==} |
| 3437 | peerDependencies: | 3450 | peerDependencies: |
| ... | @@ -4787,6 +4800,10 @@ packages: | ... | @@ -4787,6 +4800,10 @@ packages: |
| 4787 | resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} | 4800 | resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} |
| 4788 | engines: {node: '>= 14'} | 4801 | engines: {node: '>= 14'} |
| 4789 | 4802 | ||
| 4803 | agent-install@0.0.5: | ||
| 4804 | resolution: {integrity: sha512-nHlms9BkP8ZiY79HrwCGiA2DcNaXrAaJrCM/BEqQ7MEsSKyCk+2A76xPGylIfASZSZE0SaU3T0bNSg4rBPIJAQ==} | ||
| 4805 | hasBin: true | ||
| 4806 | |||
| 4790 | ajv-draft-04@1.0.0: | 4807 | ajv-draft-04@1.0.0: |
| 4791 | resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} | 4808 | resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} |
| 4792 | peerDependencies: | 4809 | peerDependencies: |
| ... | @@ -5054,6 +5071,11 @@ packages: | ... | @@ -5054,6 +5071,11 @@ packages: |
| 5054 | resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} | 5071 | resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} |
| 5055 | engines: {node: '>=0.6'} | 5072 | engines: {node: '>=0.6'} |
| 5056 | 5073 | ||
| 5074 | bippy@0.5.41: | ||
| 5075 | resolution: {integrity: sha512-jCP2pXXLhXqPrAN+iSEFZmLI4uUM4fjSqajh0K+TmM062VehfDT3ZJNkrTGyN701Z5XMejs9qAudSqkMGhSMKg==} | ||
| 5076 | peerDependencies: | ||
| 5077 | react: '>=17.0.1' | ||
| 5078 | |||
| 5057 | body-parser@2.2.2: | 5079 | body-parser@2.2.2: |
| 5058 | resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} | 5080 | resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} |
| 5059 | engines: {node: '>=18'} | 5081 | engines: {node: '>=18'} |
| ... | @@ -5244,10 +5266,18 @@ packages: | ... | @@ -5244,10 +5266,18 @@ packages: |
| 5244 | resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} | 5266 | resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} |
| 5245 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} | 5267 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} |
| 5246 | 5268 | ||
| 5269 | cli-cursor@5.0.0: | ||
| 5270 | resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} | ||
| 5271 | engines: {node: '>=18'} | ||
| 5272 | |||
| 5247 | cli-spinners@2.9.2: | 5273 | cli-spinners@2.9.2: |
| 5248 | resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} | 5274 | resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} |
| 5249 | engines: {node: '>=6'} | 5275 | engines: {node: '>=6'} |
| 5250 | 5276 | ||
| 5277 | cli-spinners@3.4.0: | ||
| 5278 | resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} | ||
| 5279 | engines: {node: '>=18.20'} | ||
| 5280 | |||
| 5251 | cli-truncate@2.1.0: | 5281 | cli-truncate@2.1.0: |
| 5252 | resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} | 5282 | resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} |
| 5253 | engines: {node: '>=8'} | 5283 | engines: {node: '>=8'} |
| ... | @@ -5321,6 +5351,10 @@ packages: | ... | @@ -5321,6 +5351,10 @@ packages: |
| 5321 | resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} | 5351 | resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} |
| 5322 | engines: {node: '>=18'} | 5352 | engines: {node: '>=18'} |
| 5323 | 5353 | ||
| 5354 | commander@14.0.3: | ||
| 5355 | resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} | ||
| 5356 | engines: {node: '>=20'} | ||
| 5357 | |||
| 5324 | commander@2.20.3: | 5358 | commander@2.20.3: |
| 5325 | resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} | 5359 | resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} |
| 5326 | 5360 | ||
| ... | @@ -6604,6 +6638,10 @@ packages: | ... | @@ -6604,6 +6638,10 @@ packages: |
| 6604 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} | 6638 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} |
| 6605 | engines: {node: '>= 4'} | 6639 | engines: {node: '>= 4'} |
| 6606 | 6640 | ||
| 6641 | ignore@7.0.5: | ||
| 6642 | resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} | ||
| 6643 | engines: {node: '>= 4'} | ||
| 6644 | |||
| 6607 | image-size@1.2.1: | 6645 | image-size@1.2.1: |
| 6608 | resolution: {integrity: sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==} | 6646 | resolution: {integrity: sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==} |
| 6609 | engines: {node: '>=16.x'} | 6647 | engines: {node: '>=16.x'} |
| ... | @@ -6725,6 +6763,10 @@ packages: | ... | @@ -6725,6 +6763,10 @@ packages: |
| 6725 | engines: {node: '>=14.16'} | 6763 | engines: {node: '>=14.16'} |
| 6726 | hasBin: true | 6764 | hasBin: true |
| 6727 | 6765 | ||
| 6766 | is-interactive@2.0.0: | ||
| 6767 | resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} | ||
| 6768 | engines: {node: '>=12'} | ||
| 6769 | |||
| 6728 | is-node-process@1.2.0: | 6770 | is-node-process@1.2.0: |
| 6729 | resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} | 6771 | resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} |
| 6730 | 6772 | ||
| ... | @@ -6742,6 +6784,10 @@ packages: | ... | @@ -6742,6 +6784,10 @@ packages: |
| 6742 | is-property@1.0.2: | 6784 | is-property@1.0.2: |
| 6743 | resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} | 6785 | resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} |
| 6744 | 6786 | ||
| 6787 | is-unicode-supported@2.1.0: | ||
| 6788 | resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} | ||
| 6789 | engines: {node: '>=18'} | ||
| 6790 | |||
| 6745 | is-wsl@2.2.0: | 6791 | is-wsl@2.2.0: |
| 6746 | resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} | 6792 | resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} |
| 6747 | engines: {node: '>=8'} | 6793 | engines: {node: '>=8'} |
| ... | @@ -7154,6 +7200,10 @@ packages: | ... | @@ -7154,6 +7200,10 @@ packages: |
| 7154 | resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==} | 7200 | resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==} |
| 7155 | engines: {node: '>=4'} | 7201 | engines: {node: '>=4'} |
| 7156 | 7202 | ||
| 7203 | log-symbols@7.0.1: | ||
| 7204 | resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} | ||
| 7205 | engines: {node: '>=18'} | ||
| 7206 | |||
| 7157 | long@5.3.2: | 7207 | long@5.3.2: |
| 7158 | resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} | 7208 | resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} |
| 7159 | 7209 | ||
| ... | @@ -7484,6 +7534,10 @@ packages: | ... | @@ -7484,6 +7534,10 @@ packages: |
| 7484 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} | 7534 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} |
| 7485 | engines: {node: '>=6'} | 7535 | engines: {node: '>=6'} |
| 7486 | 7536 | ||
| 7537 | mimic-function@5.0.1: | ||
| 7538 | resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} | ||
| 7539 | engines: {node: '>=18'} | ||
| 7540 | |||
| 7487 | mimic-response@1.0.1: | 7541 | mimic-response@1.0.1: |
| 7488 | resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} | 7542 | resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} |
| 7489 | engines: {node: '>=4'} | 7543 | engines: {node: '>=4'} |
| ... | @@ -7729,6 +7783,10 @@ packages: | ... | @@ -7729,6 +7783,10 @@ packages: |
| 7729 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} | 7783 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} |
| 7730 | engines: {node: '>=6'} | 7784 | engines: {node: '>=6'} |
| 7731 | 7785 | ||
| 7786 | onetime@7.0.0: | ||
| 7787 | resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} | ||
| 7788 | engines: {node: '>=18'} | ||
| 7789 | |||
| 7732 | oniguruma-parser@0.12.2: | 7790 | oniguruma-parser@0.12.2: |
| 7733 | resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} | 7791 | resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} |
| 7734 | 7792 | ||
| ... | @@ -7743,6 +7801,10 @@ packages: | ... | @@ -7743,6 +7801,10 @@ packages: |
| 7743 | resolution: {integrity: sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==} | 7801 | resolution: {integrity: sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==} |
| 7744 | engines: {node: '>=6'} | 7802 | engines: {node: '>=6'} |
| 7745 | 7803 | ||
| 7804 | ora@9.4.0: | ||
| 7805 | resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==} | ||
| 7806 | engines: {node: '>=20'} | ||
| 7807 | |||
| 7746 | os-paths@7.4.0: | 7808 | os-paths@7.4.0: |
| 7747 | resolution: {integrity: sha512-Ux1J4NUqC6tZayBqLN1kUlDAEvLiQlli/53sSddU4IN+h+3xxnv2HmRSMpVSvr1hvJzotfMs3ERvETGK+f4OwA==} | 7809 | resolution: {integrity: sha512-Ux1J4NUqC6tZayBqLN1kUlDAEvLiQlli/53sSddU4IN+h+3xxnv2HmRSMpVSvr1hvJzotfMs3ERvETGK+f4OwA==} |
| 7748 | engines: {node: '>= 4.0'} | 7810 | engines: {node: '>= 4.0'} |
| ... | @@ -8136,6 +8198,15 @@ packages: | ... | @@ -8136,6 +8198,15 @@ packages: |
| 8136 | peerDependencies: | 8198 | peerDependencies: |
| 8137 | react: '>=17.0.0' | 8199 | react: '>=17.0.0' |
| 8138 | 8200 | ||
| 8201 | react-grab@0.1.44: | ||
| 8202 | resolution: {integrity: sha512-bDEwBdI90ljq2lhUtPqmWis/HwYB/CvfT0m5i+P9F83Pt0Ot8o9XL8v00s9jcWzdQUlsFDzmq2FO2CHUe8JY8A==} | ||
| 8203 | hasBin: true | ||
| 8204 | peerDependencies: | ||
| 8205 | react: '>=17.0.0' | ||
| 8206 | peerDependenciesMeta: | ||
| 8207 | react: | ||
| 8208 | optional: true | ||
| 8209 | |||
| 8139 | react-is@16.13.1: | 8210 | react-is@16.13.1: |
| 8140 | resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} | 8211 | resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} |
| 8141 | 8212 | ||
| ... | @@ -8454,6 +8525,10 @@ packages: | ... | @@ -8454,6 +8525,10 @@ packages: |
| 8454 | resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} | 8525 | resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} |
| 8455 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} | 8526 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} |
| 8456 | 8527 | ||
| 8528 | restore-cursor@5.1.0: | ||
| 8529 | resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} | ||
| 8530 | engines: {node: '>=18'} | ||
| 8531 | |||
| 8457 | retext-latin@4.0.0: | 8532 | retext-latin@4.0.0: |
| 8458 | resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} | 8533 | resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} |
| 8459 | 8534 | ||
| ... | @@ -8757,6 +8832,10 @@ packages: | ... | @@ -8757,6 +8832,10 @@ packages: |
| 8757 | std-env@4.1.0: | 8832 | std-env@4.1.0: |
| 8758 | resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} | 8833 | resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} |
| 8759 | 8834 | ||
| 8835 | stdin-discarder@0.3.2: | ||
| 8836 | resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} | ||
| 8837 | engines: {node: '>=18'} | ||
| 8838 | |||
| 8760 | stream-buffers@2.2.0: | 8839 | stream-buffers@2.2.0: |
| 8761 | resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} | 8840 | resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} |
| 8762 | engines: {node: '>= 0.10.0'} | 8841 | engines: {node: '>= 0.10.0'} |
| ... | @@ -9631,6 +9710,10 @@ packages: | ... | @@ -9631,6 +9710,10 @@ packages: |
| 9631 | resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} | 9710 | resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} |
| 9632 | engines: {node: '>=18'} | 9711 | engines: {node: '>=18'} |
| 9633 | 9712 | ||
| 9713 | yoctocolors@2.1.2: | ||
| 9714 | resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} | ||
| 9715 | engines: {node: '>=18'} | ||
| 9716 | |||
| 9634 | yoga-layout@3.2.1: | 9717 | yoga-layout@3.2.1: |
| 9635 | resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} | 9718 | resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} |
| 9636 | 9719 | ||
| ... | @@ -11621,6 +11704,8 @@ snapshots: | ... | @@ -11621,6 +11704,8 @@ snapshots: |
| 11621 | dependencies: | 11704 | dependencies: |
| 11622 | hono: 4.12.25 | 11705 | hono: 4.12.25 |
| 11623 | 11706 | ||
| 11707 | '@iarna/toml@2.2.5': {} | ||
| 11708 | |||
| 11624 | '@img/colour@1.1.0': | 11709 | '@img/colour@1.1.0': |
| 11625 | optional: true | 11710 | optional: true |
| 11626 | 11711 | ||
| ... | @@ -12577,6 +12662,17 @@ snapshots: | ... | @@ -12577,6 +12662,17 @@ snapshots: |
| 12577 | optionalDependencies: | 12662 | optionalDependencies: |
| 12578 | '@types/react': 19.2.16 | 12663 | '@types/react': 19.2.16 |
| 12579 | 12664 | ||
| 12665 | '@react-grab/cli@0.1.44': | ||
| 12666 | dependencies: | ||
| 12667 | agent-install: 0.0.5 | ||
| 12668 | commander: 14.0.3 | ||
| 12669 | ignore: 7.0.5 | ||
| 12670 | ora: 9.4.0 | ||
| 12671 | package-manager-detector: 1.6.0 | ||
| 12672 | picocolors: 1.1.1 | ||
| 12673 | prompts: 2.4.2 | ||
| 12674 | tinyexec: 1.2.4 | ||
| 12675 | |||
| 12580 | '@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)': | 12676 | '@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)': |
| 12581 | dependencies: | 12677 | dependencies: |
| 12582 | react: 19.2.3 | 12678 | react: 19.2.3 |
| ... | @@ -13796,6 +13892,15 @@ snapshots: | ... | @@ -13796,6 +13892,15 @@ snapshots: |
| 13796 | 13892 | ||
| 13797 | agent-base@7.1.4: {} | 13893 | agent-base@7.1.4: {} |
| 13798 | 13894 | ||
| 13895 | agent-install@0.0.5: | ||
| 13896 | dependencies: | ||
| 13897 | '@iarna/toml': 2.2.5 | ||
| 13898 | commander: 14.0.3 | ||
| 13899 | jsonc-parser: 3.3.1 | ||
| 13900 | picocolors: 1.1.1 | ||
| 13901 | prompts: 2.4.2 | ||
| 13902 | yaml: 2.9.0 | ||
| 13903 | |||
| 13799 | ajv-draft-04@1.0.0(ajv@8.20.0): | 13904 | ajv-draft-04@1.0.0(ajv@8.20.0): |
| 13800 | optionalDependencies: | 13905 | optionalDependencies: |
| 13801 | ajv: 8.20.0 | 13906 | ajv: 8.20.0 |
| ... | @@ -14229,6 +14334,10 @@ snapshots: | ... | @@ -14229,6 +14334,10 @@ snapshots: |
| 14229 | 14334 | ||
| 14230 | big-integer@1.6.52: {} | 14335 | big-integer@1.6.52: {} |
| 14231 | 14336 | ||
| 14337 | bippy@0.5.41(react@19.2.6): | ||
| 14338 | dependencies: | ||
| 14339 | react: 19.2.6 | ||
| 14340 | |||
| 14232 | body-parser@2.2.2: | 14341 | body-parser@2.2.2: |
| 14233 | dependencies: | 14342 | dependencies: |
| 14234 | bytes: 3.1.2 | 14343 | bytes: 3.1.2 |
| ... | @@ -14451,8 +14560,14 @@ snapshots: | ... | @@ -14451,8 +14560,14 @@ snapshots: |
| 14451 | dependencies: | 14560 | dependencies: |
| 14452 | restore-cursor: 4.0.0 | 14561 | restore-cursor: 4.0.0 |
| 14453 | 14562 | ||
| 14563 | cli-cursor@5.0.0: | ||
| 14564 | dependencies: | ||
| 14565 | restore-cursor: 5.1.0 | ||
| 14566 | |||
| 14454 | cli-spinners@2.9.2: {} | 14567 | cli-spinners@2.9.2: {} |
| 14455 | 14568 | ||
| 14569 | cli-spinners@3.4.0: {} | ||
| 14570 | |||
| 14456 | cli-truncate@2.1.0: | 14571 | cli-truncate@2.1.0: |
| 14457 | dependencies: | 14572 | dependencies: |
| 14458 | slice-ansi: 3.0.0 | 14573 | slice-ansi: 3.0.0 |
| ... | @@ -14520,6 +14635,8 @@ snapshots: | ... | @@ -14520,6 +14635,8 @@ snapshots: |
| 14520 | 14635 | ||
| 14521 | commander@12.1.0: {} | 14636 | commander@12.1.0: {} |
| 14522 | 14637 | ||
| 14638 | commander@14.0.3: {} | ||
| 14639 | |||
| 14523 | commander@2.20.3: {} | 14640 | commander@2.20.3: {} |
| 14524 | 14641 | ||
| 14525 | commander@5.1.0: {} | 14642 | commander@5.1.0: {} |
| ... | @@ -15980,6 +16097,8 @@ snapshots: | ... | @@ -15980,6 +16097,8 @@ snapshots: |
| 15980 | 16097 | ||
| 15981 | ignore@5.3.2: {} | 16098 | ignore@5.3.2: {} |
| 15982 | 16099 | ||
| 16100 | ignore@7.0.5: {} | ||
| 16101 | |||
| 15983 | image-size@1.2.1: | 16102 | image-size@1.2.1: |
| 15984 | dependencies: | 16103 | dependencies: |
| 15985 | queue: 6.0.2 | 16104 | queue: 6.0.2 |
| ... | @@ -16099,6 +16218,8 @@ snapshots: | ... | @@ -16099,6 +16218,8 @@ snapshots: |
| 16099 | dependencies: | 16218 | dependencies: |
| 16100 | is-docker: 3.0.0 | 16219 | is-docker: 3.0.0 |
| 16101 | 16220 | ||
| 16221 | is-interactive@2.0.0: {} | ||
| 16222 | |||
| 16102 | is-node-process@1.2.0: {} | 16223 | is-node-process@1.2.0: {} |
| 16103 | 16224 | ||
| 16104 | is-number@7.0.0: {} | 16225 | is-number@7.0.0: {} |
| ... | @@ -16109,6 +16230,8 @@ snapshots: | ... | @@ -16109,6 +16230,8 @@ snapshots: |
| 16109 | 16230 | ||
| 16110 | is-property@1.0.2: {} | 16231 | is-property@1.0.2: {} |
| 16111 | 16232 | ||
| 16233 | is-unicode-supported@2.1.0: {} | ||
| 16234 | |||
| 16112 | is-wsl@2.2.0: | 16235 | is-wsl@2.2.0: |
| 16113 | dependencies: | 16236 | dependencies: |
| 16114 | is-docker: 2.2.1 | 16237 | is-docker: 2.2.1 |
| ... | @@ -16442,6 +16565,11 @@ snapshots: | ... | @@ -16442,6 +16565,11 @@ snapshots: |
| 16442 | dependencies: | 16565 | dependencies: |
| 16443 | chalk: 2.4.2 | 16566 | chalk: 2.4.2 |
| 16444 | 16567 | ||
| 16568 | log-symbols@7.0.1: | ||
| 16569 | dependencies: | ||
| 16570 | is-unicode-supported: 2.1.0 | ||
| 16571 | yoctocolors: 2.1.2 | ||
| 16572 | |||
| 16445 | long@5.3.2: {} | 16573 | long@5.3.2: {} |
| 16446 | 16574 | ||
| 16447 | longest-streak@3.1.0: {} | 16575 | longest-streak@3.1.0: {} |
| ... | @@ -17071,6 +17199,8 @@ snapshots: | ... | @@ -17071,6 +17199,8 @@ snapshots: |
| 17071 | 17199 | ||
| 17072 | mimic-fn@2.1.0: {} | 17200 | mimic-fn@2.1.0: {} |
| 17073 | 17201 | ||
| 17202 | mimic-function@5.0.1: {} | ||
| 17203 | |||
| 17074 | mimic-response@1.0.1: {} | 17204 | mimic-response@1.0.1: {} |
| 17075 | 17205 | ||
| 17076 | mimic-response@3.1.0: {} | 17206 | mimic-response@3.1.0: {} |
| ... | @@ -17308,6 +17438,10 @@ snapshots: | ... | @@ -17308,6 +17438,10 @@ snapshots: |
| 17308 | dependencies: | 17438 | dependencies: |
| 17309 | mimic-fn: 2.1.0 | 17439 | mimic-fn: 2.1.0 |
| 17310 | 17440 | ||
| 17441 | onetime@7.0.0: | ||
| 17442 | dependencies: | ||
| 17443 | mimic-function: 5.0.1 | ||
| 17444 | |||
| 17311 | oniguruma-parser@0.12.2: {} | 17445 | oniguruma-parser@0.12.2: {} |
| 17312 | 17446 | ||
| 17313 | oniguruma-to-es@4.3.6: | 17447 | oniguruma-to-es@4.3.6: |
| ... | @@ -17330,6 +17464,17 @@ snapshots: | ... | @@ -17330,6 +17464,17 @@ snapshots: |
| 17330 | strip-ansi: 5.2.0 | 17464 | strip-ansi: 5.2.0 |
| 17331 | wcwidth: 1.0.1 | 17465 | wcwidth: 1.0.1 |
| 17332 | 17466 | ||
| 17467 | ora@9.4.0: | ||
| 17468 | dependencies: | ||
| 17469 | chalk: 5.6.2 | ||
| 17470 | cli-cursor: 5.0.0 | ||
| 17471 | cli-spinners: 3.4.0 | ||
| 17472 | is-interactive: 2.0.0 | ||
| 17473 | is-unicode-supported: 2.1.0 | ||
| 17474 | log-symbols: 7.0.1 | ||
| 17475 | stdin-discarder: 0.3.2 | ||
| 17476 | string-width: 8.2.1 | ||
| 17477 | |||
| 17333 | os-paths@7.4.0: | 17478 | os-paths@7.4.0: |
| 17334 | optionalDependencies: | 17479 | optionalDependencies: |
| 17335 | fsevents: 2.3.3 | 17480 | fsevents: 2.3.3 |
| ... | @@ -17721,6 +17866,13 @@ snapshots: | ... | @@ -17721,6 +17866,13 @@ snapshots: |
| 17721 | dependencies: | 17866 | dependencies: |
| 17722 | react: 19.2.3 | 17867 | react: 19.2.3 |
| 17723 | 17868 | ||
| 17869 | react-grab@0.1.44(react@19.2.6): | ||
| 17870 | dependencies: | ||
| 17871 | '@react-grab/cli': 0.1.44 | ||
| 17872 | bippy: 0.5.41(react@19.2.6) | ||
| 17873 | optionalDependencies: | ||
| 17874 | react: 19.2.6 | ||
| 17875 | |||
| 17724 | react-is@16.13.1: {} | 17876 | react-is@16.13.1: {} |
| 17725 | 17877 | ||
| 17726 | react-is@17.0.2: {} | 17878 | react-is@17.0.2: {} |
| ... | @@ -18122,6 +18274,11 @@ snapshots: | ... | @@ -18122,6 +18274,11 @@ snapshots: |
| 18122 | onetime: 5.1.2 | 18274 | onetime: 5.1.2 |
| 18123 | signal-exit: 3.0.7 | 18275 | signal-exit: 3.0.7 |
| 18124 | 18276 | ||
| 18277 | restore-cursor@5.1.0: | ||
| 18278 | dependencies: | ||
| 18279 | onetime: 7.0.0 | ||
| 18280 | signal-exit: 4.1.0 | ||
| 18281 | |||
| 18125 | retext-latin@4.0.0: | 18282 | retext-latin@4.0.0: |
| 18126 | dependencies: | 18283 | dependencies: |
| 18127 | '@types/nlcst': 2.0.3 | 18284 | '@types/nlcst': 2.0.3 |
| ... | @@ -18563,6 +18720,8 @@ snapshots: | ... | @@ -18563,6 +18720,8 @@ snapshots: |
| 18563 | 18720 | ||
| 18564 | std-env@4.1.0: {} | 18721 | std-env@4.1.0: {} |
| 18565 | 18722 | ||
| 18723 | stdin-discarder@0.3.2: {} | ||
| 18724 | |||
| 18566 | stream-buffers@2.2.0: {} | 18725 | stream-buffers@2.2.0: {} |
| 18567 | 18726 | ||
| 18568 | strict-event-emitter@0.5.1: {} | 18727 | strict-event-emitter@0.5.1: {} |
| ... | @@ -19377,6 +19536,8 @@ snapshots: | ... | @@ -19377,6 +19536,8 @@ snapshots: |
| 19377 | 19536 | ||
| 19378 | yoctocolors-cjs@2.1.3: {} | 19537 | yoctocolors-cjs@2.1.3: {} |
| 19379 | 19538 | ||
| 19539 | yoctocolors@2.1.2: {} | ||
| 19540 | |||
| 19380 | yoga-layout@3.2.1: {} | 19541 | yoga-layout@3.2.1: {} |
| 19381 | 19542 | ||
| 19382 | zod-to-json-schema@3.25.2(zod@4.4.3): | 19543 | zod-to-json-schema@3.25.2(zod@4.4.3): |