From c6e40f286b554d7c3e7f083eb45b534be7466443 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 7 Sep 2026 21:20:38 -0700 Subject: [PATCH 1/3] fix(desktop): enable context menus in the browser --- .../desktop/src/electron/ElectronMenu.test.ts | 3 + apps/desktop/src/electron/ElectronMenu.ts | 2 + apps/desktop/src/window/DesktopWindow.test.ts | 131 +++++++++++++++++- apps/desktop/src/window/DesktopWindow.ts | 103 +++++++++----- 4 files changed, 200 insertions(+), 39 deletions(-) diff --git a/apps/desktop/src/electron/ElectronMenu.test.ts b/apps/desktop/src/electron/ElectronMenu.test.ts index 756274a614d7..2da7d6689599 100644 --- a/apps/desktop/src/electron/ElectronMenu.test.ts +++ b/apps/desktop/src/electron/ElectronMenu.test.ts @@ -154,9 +154,11 @@ describe("ElectronMenu", () => { buildFromTemplateMock.mockImplementation(() => ({ popup: popupMock })); const electronMenu = yield* ElectronMenu.ElectronMenu; + const frame = { routingId: 7 } as Electron.WebFrameMain; const popup = electronMenu.popupTemplate({ window: {} as Electron.BrowserWindow, template: [{ label: "Copy" }], + frame, }); assert.equal(buildFromTemplateMock.mock.calls.length, 0); @@ -166,6 +168,7 @@ describe("ElectronMenu", () => { assert.equal(buildFromTemplateMock.mock.calls.length, 1); assert.equal(popupMock.mock.calls.length, 1); + assert.strictEqual(popupMock.mock.calls[0]?.[0].frame, frame); }).pipe(Effect.provide(TestLayer)), ); diff --git a/apps/desktop/src/electron/ElectronMenu.ts b/apps/desktop/src/electron/ElectronMenu.ts index 41a286181f09..f5e20538d059 100644 --- a/apps/desktop/src/electron/ElectronMenu.ts +++ b/apps/desktop/src/electron/ElectronMenu.ts @@ -22,6 +22,7 @@ export interface ElectronMenuContextInput { export interface ElectronMenuTemplateInput { readonly window: Electron.BrowserWindow; readonly template: readonly Electron.MenuItemConstructorOptions[]; + readonly frame?: Electron.WebFrameMain; } const ElectronMenuOperation = Schema.Literals([ @@ -208,6 +209,7 @@ export const make = Effect.gen(function* () { try: () => Electron.Menu.buildFromTemplate([...input.template]).popup({ window: input.window, + ...(input.frame ? { frame: input.frame } : {}), }), catch: (cause) => new ElectronMenuOperationError({ diff --git a/apps/desktop/src/window/DesktopWindow.test.ts b/apps/desktop/src/window/DesktopWindow.test.ts index cdf2a0a27dbe..7bbb5c1da024 100644 --- a/apps/desktop/src/window/DesktopWindow.test.ts +++ b/apps/desktop/src/window/DesktopWindow.test.ts @@ -7,12 +7,14 @@ import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; import * as Logger from "effect/Logger"; import * as Option from "effect/Option"; +import * as Queue from "effect/Queue"; import * as Ref from "effect/Ref"; import * as References from "effect/References"; import * as Schema from "effect/Schema"; import * as TestClock from "effect/testing/TestClock"; import * as Electron from "electron"; +import * as NodeEvents from "node:events"; import { vi } from "vite-plus/test"; vi.mock("electron", async (importOriginal) => ({ @@ -71,6 +73,8 @@ function makeFakeBrowserWindow() { let zoomLevel = 0; const webContents = { copyImageAt: vi.fn(), + focus: vi.fn(), + isDestroyed: vi.fn(() => false), getURL: vi.fn(() => "t3code-dev://app/"), getZoomLevel: vi.fn(() => zoomLevel), setZoomLevel: vi.fn((level: number) => { @@ -214,6 +218,8 @@ function makeTestLayer(input: { bounds: DesktopAppSettings.DesktopWindowBounds, ) => Effect.Effect; readonly openedExternalUrls?: unknown[]; + readonly copiedTexts?: string[]; + readonly onPopupTemplate?: (input: ElectronMenu.ElectronMenuTemplateInput) => Effect.Effect; readonly previewZoomReapplies?: number[]; readonly onReveal?: (window: Electron.BrowserWindow) => void; }) { @@ -281,7 +287,11 @@ function makeTestLayer(input: { desktopServerExposureLayer, DesktopState.layer, electronAppLayer, - electronMenuLayer, + Layer.succeed(ElectronMenu.ElectronMenu, { + setApplicationMenu: () => Effect.void, + showContextMenu: () => Effect.succeed(Option.none()), + popupTemplate: input.onPopupTemplate ?? (() => Effect.void), + }), Layer.succeed(ElectronShell.ElectronShell, { openExternal: (url) => Effect.sync(() => { @@ -289,7 +299,10 @@ function makeTestLayer(input: { return true; }), openSystemSettings: () => Effect.succeed(true), - copyText: () => Effect.void, + copyText: (text) => + Effect.sync(() => { + input.copiedTexts?.push(text); + }), } satisfies ElectronShell.ElectronShell["Service"]), electronThemeLayer, electronWindowLayer, @@ -412,6 +425,120 @@ const captureOne = DesktopSnapShotId.make("11111111-1111-4111-8111-111111111111" const captureTwo = DesktopSnapShotId.make("22222222-2222-4222-8222-222222222222"); describe("DesktopWindow", () => { + it.effect("shows native context menus for browser guests and sign-in popups", () => + Effect.gen(function* () { + const host = makeFakeBrowserWindow(); + const popup = makeFakeBrowserWindow(); + let focusedContents: unknown = host.window.webContents; + const makeContents = () => { + const contents = Object.assign(new NodeEvents.EventEmitter(), { + isDestroyed: vi.fn(() => false), + focus: vi.fn(() => { + focusedContents = contents; + }), + copyImageAt: vi.fn(), + replaceMisspelling: vi.fn(), + }); + return contents; + }; + const guest = makeContents(); + const popupContents = makeContents(); + const popupWindow = { ...popup.window, webContents: popupContents }; + const menus = yield* Queue.unbounded<{ + input: ElectronMenu.ElectronMenuTemplateInput; + focusedContents: unknown; + }>(); + const copiedTexts: string[] = []; + const layer = makeTestLayer({ + window: host.window, + createCount: yield* Ref.make(0), + mainWindow: yield* Ref.make>(Option.none()), + copiedTexts, + onPopupTemplate: (input) => + Queue.offer(menus, { input, focusedContents }).pipe(Effect.asVoid), + }); + + yield* Effect.gen(function* () { + const desktopWindow = yield* DesktopWindow.DesktopWindow; + yield* desktopWindow.handleBackendReady(new URL("http://127.0.0.1:3773")); + const attach = host.webContentsListeners.get("did-attach-webview"); + assert.isDefined(attach); + attach({}, guest); + attach({}, guest); + guest.emit("did-create-window", popupWindow); + guest.emit("did-create-window", popupWindow); + + for (const [contents, owner] of [ + [guest, host.window], + [popupContents, popupWindow], + ] as const) { + const frame = { routingId: 7 } as Electron.WebFrameMain; + const preventDefault = vi.fn(); + const params = { + frame, + x: 12, + y: 34, + misspelledWord: "helo", + dictionarySuggestions: ["hello"], + linkURL: "", + mediaType: "none", + editFlags: { canCut: false, canCopy: true, canPaste: true, canSelectAll: true }, + }; + focusedContents = host.window.webContents; + contents.emit("context-menu", { preventDefault }, params); + const menu = yield* Queue.take(menus); + assert.strictEqual(menu.input.window, owner); + assert.strictEqual(menu.input.frame, frame); + assert.strictEqual(menu.focusedContents, contents); + assert.equal(preventDefault.mock.calls.length, 1); + assert.deepEqual( + menu.input.template.filter((item) => item.role), + [ + { role: "cut", enabled: false }, + { role: "copy", enabled: true }, + { role: "paste", enabled: true }, + { role: "selectAll", enabled: true }, + ], + ); + const correction = menu.input.template.find((item) => item.label === "hello"); + assert.isDefined(correction?.click); + correction.click({} as Electron.MenuItem, undefined, {} as Electron.KeyboardEvent); + assert.deepEqual(contents.replaceMisspelling.mock.calls, [["hello"]]); + + contents.emit( + "context-menu", + { preventDefault }, + { + ...params, + frame: null, + misspelledWord: "", + dictionarySuggestions: [], + mediaType: "image", + linkURL: "https://example.com/image.png", + }, + ); + const imageMenu = (yield* Queue.take(menus)).input; + assert.isUndefined(imageMenu.frame); + const copyImage = imageMenu.template.find((item) => item.label === "Copy Image"); + const copyLink = imageMenu.template.find((item) => item.label === "Copy Link"); + assert.isDefined(copyImage?.click); + assert.isDefined(copyLink?.click); + copyImage.click({} as Electron.MenuItem, undefined, {} as Electron.KeyboardEvent); + copyLink.click({} as Electron.MenuItem, undefined, {} as Electron.KeyboardEvent); + assert.deepEqual(contents.copyImageAt.mock.calls, [[12, 34]]); + assert.equal(copiedTexts.at(-1), "https://example.com/image.png"); + + contents.isDestroyed.mockReturnValue(true); + correction.click({} as Electron.MenuItem, undefined, {} as Electron.KeyboardEvent); + copyImage.click({} as Electron.MenuItem, undefined, {} as Electron.KeyboardEvent); + assert.equal(contents.replaceMisspelling.mock.calls.length, 1); + assert.equal(contents.copyImageAt.mock.calls.length, 1); + assert.equal(yield* Queue.size(menus), 0); + } + }).pipe(Effect.provide(layer)); + }), + ); + it("leaves fullscreen before concealing a pending quit", () => { const fakeWindow = makeFakeBrowserWindow(); diff --git a/apps/desktop/src/window/DesktopWindow.ts b/apps/desktop/src/window/DesktopWindow.ts index 9f576a05dd11..0a966ec36e4d 100644 --- a/apps/desktop/src/window/DesktopWindow.ts +++ b/apps/desktop/src/window/DesktopWindow.ts @@ -505,52 +505,81 @@ export const make = Effect.gen(function* () { webPreferences.contextIsolation = false; }); - window.webContents.on("context-menu", (event, params) => { - event.preventDefault(); + const contextMenuContents = new WeakSet(); + const installContextMenu = ( + ownerWindow: Electron.BrowserWindow, + contents: Electron.WebContents, + ): void => { + if (contextMenuContents.has(contents)) return; + contextMenuContents.add(contents); + contents.on("context-menu", (event, params) => { + event.preventDefault(); + if (contents.isDestroyed() || ownerWindow.isDestroyed()) return; + // Native editing roles act on the focused contents, which may still be + // the host renderer when the user right-clicks inside a browser guest. + contents.focus(); + + const menuTemplate: Electron.MenuItemConstructorOptions[] = []; + + if (params.misspelledWord) { + for (const suggestion of params.dictionarySuggestions.slice(0, 5)) { + menuTemplate.push({ + label: suggestion, + click: () => { + if (!contents.isDestroyed()) contents.replaceMisspelling(suggestion); + }, + }); + } + if (params.dictionarySuggestions.length === 0) { + menuTemplate.push({ label: "No suggestions", enabled: false }); + } + menuTemplate.push({ type: "separator" }); + } - const menuTemplate: Electron.MenuItemConstructorOptions[] = []; + if (Option.isSome(ElectronShell.parseSafeExternalUrl(params.linkURL))) { + menuTemplate.push( + { + label: "Copy Link", + click: () => { + void runPromise(electronShell.copyText(params.linkURL)); + }, + }, + { type: "separator" }, + ); + } - if (params.misspelledWord) { - for (const suggestion of params.dictionarySuggestions.slice(0, 5)) { + if (params.mediaType === "image") { menuTemplate.push({ - label: suggestion, - click: () => window.webContents.replaceMisspelling(suggestion), + label: "Copy Image", + click: () => { + if (!contents.isDestroyed()) contents.copyImageAt(params.x, params.y); + }, }); + menuTemplate.push({ type: "separator" }); } - if (params.dictionarySuggestions.length === 0) { - menuTemplate.push({ label: "No suggestions", enabled: false }); - } - menuTemplate.push({ type: "separator" }); - } - if (Option.isSome(ElectronShell.parseSafeExternalUrl(params.linkURL))) { menuTemplate.push( - { - label: "Copy Link", - click: () => { - void runPromise(electronShell.copyText(params.linkURL)); - }, - }, - { type: "separator" }, + { role: "cut", enabled: params.editFlags.canCut }, + { role: "copy", enabled: params.editFlags.canCopy }, + { role: "paste", enabled: params.editFlags.canPaste }, + { role: "selectAll", enabled: params.editFlags.canSelectAll }, ); - } - - if (params.mediaType === "image") { - menuTemplate.push({ - label: "Copy Image", - click: () => window.webContents.copyImageAt(params.x, params.y), - }); - menuTemplate.push({ type: "separator" }); - } - - menuTemplate.push( - { role: "cut", enabled: params.editFlags.canCut }, - { role: "copy", enabled: params.editFlags.canCopy }, - { role: "paste", enabled: params.editFlags.canPaste }, - { role: "selectAll", enabled: params.editFlags.canSelectAll }, - ); - void runPromise(electronMenu.popupTemplate({ window, template: menuTemplate })); + void runPromise( + electronMenu.popupTemplate({ + window: ownerWindow, + template: menuTemplate, + ...(params.frame ? { frame: params.frame } : {}), + }), + ); + }); + contents.on("did-create-window", (popup) => { + installContextMenu(popup, popup.webContents); + }); + }; + installContextMenu(window, window.webContents); + window.webContents.on("did-attach-webview", (_event, contents) => { + installContextMenu(window, contents); }); window.webContents.setWindowOpenHandler(({ url }) => { From 63094c687c8d0066af650738fd7cb41f456ccc66 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 7 Sep 2026 21:34:58 -0700 Subject: [PATCH 2/3] chore(scripts): add declarations for build helper modules --- scripts/lib/brand-assets.d.ts | 44 ++++++++ scripts/lib/brand-assets.test.d.ts | 1 + scripts/lib/build-target-arch.d.ts | 12 ++ scripts/lib/build-target-arch.test.d.ts | 1 + scripts/lib/cli-external-packages.d.ts | 112 +++++++++++++++++++ scripts/lib/cli-external-packages.test.d.ts | 1 + scripts/lib/dev-share.d.ts | 117 ++++++++++++++++++++ scripts/lib/dev-share.test.d.ts | 1 + scripts/lib/icon-export.d.ts | 11 ++ scripts/lib/icon-export.test.d.ts | 1 + scripts/lib/public-config.d.ts | 22 ++++ scripts/lib/public-config.test.d.ts | 1 + scripts/lib/reference-repos.d.ts | 10 ++ scripts/lib/resolve-catalog.d.ts | 11 ++ scripts/lib/update-manifest.d.ts | 28 +++++ 15 files changed, 373 insertions(+) create mode 100644 scripts/lib/brand-assets.d.ts create mode 100644 scripts/lib/brand-assets.test.d.ts create mode 100644 scripts/lib/build-target-arch.d.ts create mode 100644 scripts/lib/build-target-arch.test.d.ts create mode 100644 scripts/lib/cli-external-packages.d.ts create mode 100644 scripts/lib/cli-external-packages.test.d.ts create mode 100644 scripts/lib/dev-share.d.ts create mode 100644 scripts/lib/dev-share.test.d.ts create mode 100644 scripts/lib/icon-export.d.ts create mode 100644 scripts/lib/icon-export.test.d.ts create mode 100644 scripts/lib/public-config.d.ts create mode 100644 scripts/lib/public-config.test.d.ts create mode 100644 scripts/lib/reference-repos.d.ts create mode 100644 scripts/lib/resolve-catalog.d.ts create mode 100644 scripts/lib/update-manifest.d.ts diff --git a/scripts/lib/brand-assets.d.ts b/scripts/lib/brand-assets.d.ts new file mode 100644 index 000000000000..4f508077edbe --- /dev/null +++ b/scripts/lib/brand-assets.d.ts @@ -0,0 +1,44 @@ +export declare const BRAND_ASSET_PATHS: { + readonly developmentIconComposerProject: "assets/dev/app-icon.icon"; + readonly developmentIosIconPng: "assets/dev/blueprint-ios-1024.png"; + readonly developmentUniversalIconPng: "assets/dev/blueprint-universal-1024.png"; + readonly productionIconComposerProject: "assets/prod/app-icon.icon"; + readonly productionIosIconPng: "assets/prod/black-ios-1024.png"; + readonly productionMacIconPng: "assets/prod/black-macos-1024.png"; + readonly productionLinuxIconPng: "assets/prod/black-universal-1024.png"; + readonly productionWindowsIconIco: "assets/prod/t3-black-windows.ico"; + readonly productionWebFaviconIco: "assets/prod/t3-black-web-favicon.ico"; + readonly productionWebFavicon16Png: "assets/prod/t3-black-web-favicon-16x16.png"; + readonly productionWebFavicon32Png: "assets/prod/t3-black-web-favicon-32x32.png"; + readonly productionWebAppleTouchIconPng: "assets/prod/t3-black-web-apple-touch-180.png"; + readonly nightlyIconComposerProject: "assets/nightly/app-icon.icon"; + readonly nightlyIosIconPng: "assets/nightly/nightly-ios-1024.png"; + readonly nightlyMacIconPng: "assets/nightly/nightly-macos-1024.png"; + readonly nightlyLinuxIconPng: "assets/nightly/nightly-universal-1024.png"; + readonly nightlyWindowsIconIco: "assets/nightly/nightly-windows.ico"; + readonly nightlyWebFaviconIco: "assets/nightly/nightly-web-favicon.ico"; + readonly nightlyWebFavicon16Png: "assets/nightly/nightly-web-favicon-16x16.png"; + readonly nightlyWebFavicon32Png: "assets/nightly/nightly-web-favicon-32x32.png"; + readonly nightlyWebAppleTouchIconPng: "assets/nightly/nightly-web-apple-touch-180.png"; + readonly developmentDesktopIconPng: "assets/dev/blueprint-macos-1024.png"; + readonly developmentWindowsIconIco: "assets/dev/blueprint-windows.ico"; + readonly developmentWebFaviconIco: "assets/dev/blueprint-web-favicon.ico"; + readonly developmentWebFavicon16Png: "assets/dev/blueprint-web-favicon-16x16.png"; + readonly developmentWebFavicon32Png: "assets/dev/blueprint-web-favicon-32x32.png"; + readonly developmentWebAppleTouchIconPng: "assets/dev/blueprint-web-apple-touch-180.png"; +}; +export type WebAssetBrand = "development" | "nightly" | "production"; +export declare const WEB_ASSET_CHANNELS: readonly ["latest", "nightly"]; +export type WebAssetChannel = (typeof WEB_ASSET_CHANNELS)[number]; +export declare function resolveWebAssetBrandForChannel(channel: WebAssetChannel): WebAssetBrand; +export declare function resolveWebAssetBrandForPackageVersion(version: string): WebAssetBrand; +export interface IconOverride { + readonly sourceRelativePath: string; + readonly targetRelativePath: string; +} +export declare function resolveWebIconOverrides( + brand: WebAssetBrand, + targetDirectory: string, +): ReadonlyArray; +export declare const DEVELOPMENT_ICON_OVERRIDES: readonly IconOverride[]; +export declare const DEVELOPMENT_PUBLIC_ICON_OVERRIDES: readonly IconOverride[]; diff --git a/scripts/lib/brand-assets.test.d.ts b/scripts/lib/brand-assets.test.d.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/scripts/lib/brand-assets.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/scripts/lib/build-target-arch.d.ts b/scripts/lib/build-target-arch.d.ts new file mode 100644 index 000000000000..d62773798629 --- /dev/null +++ b/scripts/lib/build-target-arch.d.ts @@ -0,0 +1,12 @@ +import * as Config from "effect/Config"; +import * as Effect from "effect/Effect"; +export type BuildArch = "arm64" | "x64" | "universal"; +export type BuildPlatform = "mac" | "linux" | "win"; +interface PlatformConfig { + readonly archChoices: ReadonlyArray; +} +export declare const getDefaultBuildArch: ( + platform: BuildPlatform, + platformConfig: PlatformConfig, +) => Effect.Effect; +export {}; diff --git a/scripts/lib/build-target-arch.test.d.ts b/scripts/lib/build-target-arch.test.d.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/scripts/lib/build-target-arch.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/scripts/lib/cli-external-packages.d.ts b/scripts/lib/cli-external-packages.d.ts new file mode 100644 index 000000000000..d549d7ca9649 --- /dev/null +++ b/scripts/lib/cli-external-packages.d.ts @@ -0,0 +1,112 @@ +/** + * The single source of truth for packages the server CLI bundle must NOT inline. + * + * Two consumers derive from this list, and they must never disagree: + * + * - apps/server/vite.config.ts decides what stays external to the bundle. + * - scripts/build-desktop-artifact.ts selects the runtime dependency roots for + * the Windows server sidecar. + * + * A runtime package that is external but absent from the sidecar fails as soon + * as Node resolves it from the emitted bundle. Keeping both consumers on one + * list prevents packaging from drifting away from the bundle boundary. + * + * Entries are matched as prefixes (`id.startsWith(prefix)`), so they also cover + * a package's platform-specific siblings — `node-gyp-build` covers + * `node-gyp-build-optional-packages`, `@yuuang/` covers every `ffi-rs-*` binding. + */ +/** + * External because Node actually loads them from disk at runtime. + * + * Native addons (.node), the JS wrappers that dlopen them by real path, and — + * critically — the ordinary JS packages those wrappers require. An external + * package is loaded from the real filesystem, so its own `require` also + * resolves from the real filesystem; a dependency that was bundled away exists + * only inside the emitted bundle and is unreachable there. This closure is + * enforced by a test, not by inspection. + */ +export declare const CLI_RUNTIME_EXTERNAL_PREFIXES: readonly [ + "node-pty", + "ffi-rs", + "@yuuang/", + "@ff-labs/", + "@clerk/electron-passkeys", + "@msgpackr-extract/", + "msgpackr-extract", + "node-gyp-build", + "node-addon-api", + "detect-libc", + "bufferutil", + "utf-8-validate", +]; +/** + * External only so the bundler never has to resolve them. + * + * These are reached through a runtime-conditional dynamic import that Node + * never takes, and they resolve `bun:*` specifiers that do not exist when + * bundling for Node. Because Node never loads them, their dependency closure + * does not need to be external — only the entry point must stay unbundled. + */ +export declare const CLI_BUILD_ONLY_EXTERNAL_PREFIXES: readonly [ + "@effect/platform-bun", + "@effect/sql-sqlite-bun", +]; +export declare const CLI_EXTERNAL_PACKAGE_PREFIXES: readonly [ + "node-pty", + "ffi-rs", + "@yuuang/", + "@ff-labs/", + "@clerk/electron-passkeys", + "@msgpackr-extract/", + "msgpackr-extract", + "node-gyp-build", + "node-addon-api", + "detect-libc", + "bufferutil", + "utf-8-validate", + "@effect/platform-bun", + "@effect/sql-sqlite-bun", +]; +export declare function isRuntimeExternalCliDependency(id: string): boolean; +/** + * True when `id` must stay out of the bundle. + * + * This has to be wired to the bundler's `neverBundle`, not just to + * `alwaysBundle`. `alwaysBundle` only forces packages IN — returning false from + * it means "no opinion", and the default then applies: a declared dependency + * stays external, but a transitive one gets bundled. That is how + * msgpackr-extract, node-gyp-build-optional-packages and detect-libc ended up + * inlined while node-pty (a declared dependency) stayed external. + */ +export declare function isExternalCliDependency(id: string): boolean; +/** True when the CLI bundle should inline `id` rather than leave it external. */ +export declare function shouldBundleCliDependency(id: string): boolean; +/** Select direct dependency roots whose runtime closure belongs in the sidecar. */ +export declare function selectCliRuntimeExternalDependencies( + dependencies: Readonly>, +): Record; +/** + * Scan an emitted bundle chunk for runtime-external packages that were inlined. + * + * Configuring the bundler is not the same as checking what it produced. The + * `alwaysBundle` predicate only forces packages IN; returning false from it + * means "no opinion", so a transitive dependency still gets bundled by default. + * msgpackr-extract, node-gyp-build-optional-packages and detect-libc were + * inlined that way while every list-based test passed, which is why this reads + * the artifact instead. + * + * `regionCount` is reported so the caller can tell "nothing was inlined" apart + * from "the marker format changed and this scan no longer sees anything". + * + * `inlinedPackages` is every package seen in a region, which lets the caller + * check the opposite direction too. Verifying only that externals are absent + * would still pass if the bundler reverted to leaving everything external: the + * scan would see source-file regions, report nothing inlined, and the packaged + * backends would then fail with ERR_MODULE_NOT_FOUND because those packages + * are not in the selected sidecar closure either. + */ +export declare function findInlinedExternalPackages(source: string): { + readonly regionCount: number; + readonly inlined: ReadonlyArray; + readonly inlinedPackages: ReadonlyArray; +}; diff --git a/scripts/lib/cli-external-packages.test.d.ts b/scripts/lib/cli-external-packages.test.d.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/scripts/lib/cli-external-packages.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/scripts/lib/dev-share.d.ts b/scripts/lib/dev-share.d.ts new file mode 100644 index 000000000000..2ee05df72139 --- /dev/null +++ b/scripts/lib/dev-share.d.ts @@ -0,0 +1,117 @@ +/** + * Shares a running dev server on the local tailnet via `tailscale serve`, so it + * can be opened from a phone, another laptop, or by whoever is reviewing the + * work. + * + * Thin wrapper over `@t3tools/tailscale` (the same client the server's own + * `--tailscale-serve` uses). What it adds is dev-share semantics: replacing a + * stale mapping left by a killed run, and refusing to serve over routes it + * could not remove. + * + * Because browser dev is single-origin (Vite proxies the backend — see + * `resolveDevProxyTarget` in apps/web/vite.config.ts), one proxy rule covering + * the web port is enough; the backend needs no mapping of its own. + */ +import { type TailscaleCommandError } from "@t3tools/tailscale"; +import * as Effect from "effect/Effect"; +import * as Schema from "effect/Schema"; +import type { ChildProcessSpawner } from "effect/unstable/process"; +declare const TailscaleUnavailableError_base: Schema.Class< + TailscaleUnavailableError, + Schema.TaggedStruct< + "TailscaleUnavailableError", + { + readonly cause: Schema.Defect; + } + >, + import("effect/Cause").YieldableError +>; +/** + * Three distinct failures, three classes: each has its own caller-visible + * message and its own remedy, and `shareDevServer` chooses between them + * structurally. A single error with a `reason` discriminator would encode that + * distinction twice and put a lookup table in the `message` getter. + * + * Each wraps a real underlying failure and so keeps it as `cause`; the message + * is derived only from the structural fields, never from `cause.message`. + */ +export declare class TailscaleUnavailableError extends TailscaleUnavailableError_base { + get message(): string; + get hint(): string; +} +declare const TailnetNameMissingError_base: Schema.Class< + TailnetNameMissingError, + Schema.TaggedStruct<"TailnetNameMissingError", {}>, + import("effect/Cause").YieldableError +>; +/** No underlying failure: the status read succeeded and simply had no name. */ +export declare class TailnetNameMissingError extends TailnetNameMissingError_base { + get message(): string; + get hint(): string; +} +declare const DevServeFailedError_base: Schema.Class< + DevServeFailedError, + Schema.TaggedStruct< + "DevServeFailedError", + { + readonly stage: Schema.Literals; + readonly webPort: Schema.Number; + readonly explanation: Schema.optional; + readonly cause: Schema.optional; + } + >, + import("effect/Cause").YieldableError +>; +/** + * `stage` is a genuine multi-value discriminator: both stages share the same + * semantics (a `tailscale serve` invocation failed for this port) and differ + * only in which one, which the message states plainly. + */ +export declare class DevServeFailedError extends DevServeFailedError_base { + get message(): string; + get hint(): undefined; +} +export declare const DevShareError: Schema.Union< + readonly [ + typeof TailscaleUnavailableError, + typeof TailnetNameMissingError, + typeof DevServeFailedError, + ] +>; +export type DevShareError = typeof DevShareError.Type; +export declare const isDevShareError: ( + input: I, +) => input is I & (DevServeFailedError | TailnetNameMissingError | TailscaleUnavailableError); +/** + * Removes any mapping for `webPort`, reporting whether the port is now clear. + * + * Runs uninterruptibly: this is called from a finalizer on the way out of an + * interrupted program, and cancelling the cleanup subprocess would leave + * exactly the stale mapping it exists to remove. + */ +export declare const unshareDevServer: (webPort: number) => Effect.Effect< + { + readonly cleared: boolean; + readonly explanation?: string | undefined; + readonly cause?: TailscaleCommandError | undefined; + }, + never, + ChildProcessSpawner.ChildProcessSpawner +>; +export interface DevShareResult { + readonly url: string; + readonly host: string; +} +/** + * Publishes `webPort` on the tailnet at the same port number and returns the + * resulting HTTPS URL. Idempotent: re-running replaces any existing mapping. + */ +export declare const shareDevServer: (input: { readonly webPort: number }) => Effect.Effect< + { + url: string; + host: string; + }, + DevServeFailedError | TailnetNameMissingError | TailscaleUnavailableError, + ChildProcessSpawner.ChildProcessSpawner +>; +export {}; diff --git a/scripts/lib/dev-share.test.d.ts b/scripts/lib/dev-share.test.d.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/scripts/lib/dev-share.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/scripts/lib/icon-export.d.ts b/scripts/lib/icon-export.d.ts new file mode 100644 index 000000000000..c0084c016b5f --- /dev/null +++ b/scripts/lib/icon-export.d.ts @@ -0,0 +1,11 @@ +export declare const WINDOWS_ICON_SIZES: readonly [16, 24, 32, 48, 64, 128, 256]; +export interface PngIconImage { + readonly size: number; + readonly contents: Buffer; +} +export declare function readPngDimensions(contents: Buffer): { + readonly width: number; + readonly height: number; +}; +/** Encodes PNG renditions directly into a modern, multi-resolution ICO file. */ +export declare function encodePngIco(images: ReadonlyArray): Buffer; diff --git a/scripts/lib/icon-export.test.d.ts b/scripts/lib/icon-export.test.d.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/scripts/lib/icon-export.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/scripts/lib/public-config.d.ts b/scripts/lib/public-config.d.ts new file mode 100644 index 000000000000..8da6e580e662 --- /dev/null +++ b/scripts/lib/public-config.d.ts @@ -0,0 +1,22 @@ +export interface T3CodePublicConfig { + readonly clerkPublishableKey: string | undefined; + readonly clerkJwtTemplate: string | undefined; + readonly clerkCliOAuthClientId: string | undefined; + readonly relayUrl: string | undefined; + readonly mobileOtlpTracesUrl: string | undefined; + readonly mobileOtlpTracesDataset: string | undefined; + readonly mobileOtlpTracesToken: string | undefined; + readonly relayClientOtlpTracesUrl: string | undefined; + readonly relayClientOtlpTracesDataset: string | undefined; + readonly relayClientOtlpTracesToken: string | undefined; +} +type Environment = Readonly>; +export declare function loadRepoEnv({ + baseEnv, + repoRoot, +}?: { + readonly baseEnv?: Environment; + readonly repoRoot?: string; +}): Record; +export declare function resolvePublicConfig(...sources: readonly Environment[]): T3CodePublicConfig; +export {}; diff --git a/scripts/lib/public-config.test.d.ts b/scripts/lib/public-config.test.d.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/scripts/lib/public-config.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/scripts/lib/reference-repos.d.ts b/scripts/lib/reference-repos.d.ts new file mode 100644 index 000000000000..063c678d777d --- /dev/null +++ b/scripts/lib/reference-repos.d.ts @@ -0,0 +1,10 @@ +export interface ReferenceRepo { + readonly id: string; + readonly prefix: string; + readonly repository: string; + readonly latestRef: string; + readonly versionSourcePath: string; + readonly packageVersionPath: ReadonlyArray; + readonly versionTagPrefix: string; +} +export declare const referenceRepos: ReadonlyArray; diff --git a/scripts/lib/resolve-catalog.d.ts b/scripts/lib/resolve-catalog.d.ts new file mode 100644 index 000000000000..9303f3eee0d1 --- /dev/null +++ b/scripts/lib/resolve-catalog.d.ts @@ -0,0 +1,11 @@ +/** + * Resolve `catalog:` dependency specs using the workspace catalog. + * + * Pure function: returns a new record with every `catalog:…` value replaced by + * the concrete version string found in `catalog`. Throws on missing entries. + */ +export declare function resolveCatalogDependencies( + dependencies: Record, + catalog: Record, + workspacePackage: string, +): Record; diff --git a/scripts/lib/update-manifest.d.ts b/scripts/lib/update-manifest.d.ts new file mode 100644 index 000000000000..fe27bac4f6fc --- /dev/null +++ b/scripts/lib/update-manifest.d.ts @@ -0,0 +1,28 @@ +export interface UpdateManifestFile { + readonly url: string; + readonly sha512: string; + readonly size: number; +} +export type UpdateManifestScalar = string | number | boolean; +export interface UpdateManifest { + readonly version: string; + readonly releaseDate: string; + readonly files: ReadonlyArray; + readonly extras: Readonly>; +} +export declare function parseUpdateManifest( + raw: string, + sourcePath: string, + platformLabel: string, +): UpdateManifest; +export declare function mergeUpdateManifests( + primary: UpdateManifest, + secondary: UpdateManifest, + platformLabel: string, +): UpdateManifest; +export declare function serializeUpdateManifest( + manifest: UpdateManifest, + options: { + readonly platformLabel: string; + }, +): string; From 267907e0c19465d59bd9b5e88f83f8dc94ccbf3e Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 7 Sep 2026 21:37:33 -0700 Subject: [PATCH 3/3] chore: remove generated typecheck declarations --- scripts/lib/brand-assets.d.ts | 44 -------- scripts/lib/brand-assets.test.d.ts | 1 - scripts/lib/build-target-arch.d.ts | 12 -- scripts/lib/build-target-arch.test.d.ts | 1 - scripts/lib/cli-external-packages.d.ts | 112 ------------------- scripts/lib/cli-external-packages.test.d.ts | 1 - scripts/lib/dev-share.d.ts | 117 -------------------- scripts/lib/dev-share.test.d.ts | 1 - scripts/lib/icon-export.d.ts | 11 -- scripts/lib/icon-export.test.d.ts | 1 - scripts/lib/public-config.d.ts | 22 ---- scripts/lib/public-config.test.d.ts | 1 - scripts/lib/reference-repos.d.ts | 10 -- scripts/lib/resolve-catalog.d.ts | 11 -- scripts/lib/update-manifest.d.ts | 28 ----- 15 files changed, 373 deletions(-) delete mode 100644 scripts/lib/brand-assets.d.ts delete mode 100644 scripts/lib/brand-assets.test.d.ts delete mode 100644 scripts/lib/build-target-arch.d.ts delete mode 100644 scripts/lib/build-target-arch.test.d.ts delete mode 100644 scripts/lib/cli-external-packages.d.ts delete mode 100644 scripts/lib/cli-external-packages.test.d.ts delete mode 100644 scripts/lib/dev-share.d.ts delete mode 100644 scripts/lib/dev-share.test.d.ts delete mode 100644 scripts/lib/icon-export.d.ts delete mode 100644 scripts/lib/icon-export.test.d.ts delete mode 100644 scripts/lib/public-config.d.ts delete mode 100644 scripts/lib/public-config.test.d.ts delete mode 100644 scripts/lib/reference-repos.d.ts delete mode 100644 scripts/lib/resolve-catalog.d.ts delete mode 100644 scripts/lib/update-manifest.d.ts diff --git a/scripts/lib/brand-assets.d.ts b/scripts/lib/brand-assets.d.ts deleted file mode 100644 index 4f508077edbe..000000000000 --- a/scripts/lib/brand-assets.d.ts +++ /dev/null @@ -1,44 +0,0 @@ -export declare const BRAND_ASSET_PATHS: { - readonly developmentIconComposerProject: "assets/dev/app-icon.icon"; - readonly developmentIosIconPng: "assets/dev/blueprint-ios-1024.png"; - readonly developmentUniversalIconPng: "assets/dev/blueprint-universal-1024.png"; - readonly productionIconComposerProject: "assets/prod/app-icon.icon"; - readonly productionIosIconPng: "assets/prod/black-ios-1024.png"; - readonly productionMacIconPng: "assets/prod/black-macos-1024.png"; - readonly productionLinuxIconPng: "assets/prod/black-universal-1024.png"; - readonly productionWindowsIconIco: "assets/prod/t3-black-windows.ico"; - readonly productionWebFaviconIco: "assets/prod/t3-black-web-favicon.ico"; - readonly productionWebFavicon16Png: "assets/prod/t3-black-web-favicon-16x16.png"; - readonly productionWebFavicon32Png: "assets/prod/t3-black-web-favicon-32x32.png"; - readonly productionWebAppleTouchIconPng: "assets/prod/t3-black-web-apple-touch-180.png"; - readonly nightlyIconComposerProject: "assets/nightly/app-icon.icon"; - readonly nightlyIosIconPng: "assets/nightly/nightly-ios-1024.png"; - readonly nightlyMacIconPng: "assets/nightly/nightly-macos-1024.png"; - readonly nightlyLinuxIconPng: "assets/nightly/nightly-universal-1024.png"; - readonly nightlyWindowsIconIco: "assets/nightly/nightly-windows.ico"; - readonly nightlyWebFaviconIco: "assets/nightly/nightly-web-favicon.ico"; - readonly nightlyWebFavicon16Png: "assets/nightly/nightly-web-favicon-16x16.png"; - readonly nightlyWebFavicon32Png: "assets/nightly/nightly-web-favicon-32x32.png"; - readonly nightlyWebAppleTouchIconPng: "assets/nightly/nightly-web-apple-touch-180.png"; - readonly developmentDesktopIconPng: "assets/dev/blueprint-macos-1024.png"; - readonly developmentWindowsIconIco: "assets/dev/blueprint-windows.ico"; - readonly developmentWebFaviconIco: "assets/dev/blueprint-web-favicon.ico"; - readonly developmentWebFavicon16Png: "assets/dev/blueprint-web-favicon-16x16.png"; - readonly developmentWebFavicon32Png: "assets/dev/blueprint-web-favicon-32x32.png"; - readonly developmentWebAppleTouchIconPng: "assets/dev/blueprint-web-apple-touch-180.png"; -}; -export type WebAssetBrand = "development" | "nightly" | "production"; -export declare const WEB_ASSET_CHANNELS: readonly ["latest", "nightly"]; -export type WebAssetChannel = (typeof WEB_ASSET_CHANNELS)[number]; -export declare function resolveWebAssetBrandForChannel(channel: WebAssetChannel): WebAssetBrand; -export declare function resolveWebAssetBrandForPackageVersion(version: string): WebAssetBrand; -export interface IconOverride { - readonly sourceRelativePath: string; - readonly targetRelativePath: string; -} -export declare function resolveWebIconOverrides( - brand: WebAssetBrand, - targetDirectory: string, -): ReadonlyArray; -export declare const DEVELOPMENT_ICON_OVERRIDES: readonly IconOverride[]; -export declare const DEVELOPMENT_PUBLIC_ICON_OVERRIDES: readonly IconOverride[]; diff --git a/scripts/lib/brand-assets.test.d.ts b/scripts/lib/brand-assets.test.d.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/scripts/lib/brand-assets.test.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/scripts/lib/build-target-arch.d.ts b/scripts/lib/build-target-arch.d.ts deleted file mode 100644 index d62773798629..000000000000 --- a/scripts/lib/build-target-arch.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as Config from "effect/Config"; -import * as Effect from "effect/Effect"; -export type BuildArch = "arm64" | "x64" | "universal"; -export type BuildPlatform = "mac" | "linux" | "win"; -interface PlatformConfig { - readonly archChoices: ReadonlyArray; -} -export declare const getDefaultBuildArch: ( - platform: BuildPlatform, - platformConfig: PlatformConfig, -) => Effect.Effect; -export {}; diff --git a/scripts/lib/build-target-arch.test.d.ts b/scripts/lib/build-target-arch.test.d.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/scripts/lib/build-target-arch.test.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/scripts/lib/cli-external-packages.d.ts b/scripts/lib/cli-external-packages.d.ts deleted file mode 100644 index d549d7ca9649..000000000000 --- a/scripts/lib/cli-external-packages.d.ts +++ /dev/null @@ -1,112 +0,0 @@ -/** - * The single source of truth for packages the server CLI bundle must NOT inline. - * - * Two consumers derive from this list, and they must never disagree: - * - * - apps/server/vite.config.ts decides what stays external to the bundle. - * - scripts/build-desktop-artifact.ts selects the runtime dependency roots for - * the Windows server sidecar. - * - * A runtime package that is external but absent from the sidecar fails as soon - * as Node resolves it from the emitted bundle. Keeping both consumers on one - * list prevents packaging from drifting away from the bundle boundary. - * - * Entries are matched as prefixes (`id.startsWith(prefix)`), so they also cover - * a package's platform-specific siblings — `node-gyp-build` covers - * `node-gyp-build-optional-packages`, `@yuuang/` covers every `ffi-rs-*` binding. - */ -/** - * External because Node actually loads them from disk at runtime. - * - * Native addons (.node), the JS wrappers that dlopen them by real path, and — - * critically — the ordinary JS packages those wrappers require. An external - * package is loaded from the real filesystem, so its own `require` also - * resolves from the real filesystem; a dependency that was bundled away exists - * only inside the emitted bundle and is unreachable there. This closure is - * enforced by a test, not by inspection. - */ -export declare const CLI_RUNTIME_EXTERNAL_PREFIXES: readonly [ - "node-pty", - "ffi-rs", - "@yuuang/", - "@ff-labs/", - "@clerk/electron-passkeys", - "@msgpackr-extract/", - "msgpackr-extract", - "node-gyp-build", - "node-addon-api", - "detect-libc", - "bufferutil", - "utf-8-validate", -]; -/** - * External only so the bundler never has to resolve them. - * - * These are reached through a runtime-conditional dynamic import that Node - * never takes, and they resolve `bun:*` specifiers that do not exist when - * bundling for Node. Because Node never loads them, their dependency closure - * does not need to be external — only the entry point must stay unbundled. - */ -export declare const CLI_BUILD_ONLY_EXTERNAL_PREFIXES: readonly [ - "@effect/platform-bun", - "@effect/sql-sqlite-bun", -]; -export declare const CLI_EXTERNAL_PACKAGE_PREFIXES: readonly [ - "node-pty", - "ffi-rs", - "@yuuang/", - "@ff-labs/", - "@clerk/electron-passkeys", - "@msgpackr-extract/", - "msgpackr-extract", - "node-gyp-build", - "node-addon-api", - "detect-libc", - "bufferutil", - "utf-8-validate", - "@effect/platform-bun", - "@effect/sql-sqlite-bun", -]; -export declare function isRuntimeExternalCliDependency(id: string): boolean; -/** - * True when `id` must stay out of the bundle. - * - * This has to be wired to the bundler's `neverBundle`, not just to - * `alwaysBundle`. `alwaysBundle` only forces packages IN — returning false from - * it means "no opinion", and the default then applies: a declared dependency - * stays external, but a transitive one gets bundled. That is how - * msgpackr-extract, node-gyp-build-optional-packages and detect-libc ended up - * inlined while node-pty (a declared dependency) stayed external. - */ -export declare function isExternalCliDependency(id: string): boolean; -/** True when the CLI bundle should inline `id` rather than leave it external. */ -export declare function shouldBundleCliDependency(id: string): boolean; -/** Select direct dependency roots whose runtime closure belongs in the sidecar. */ -export declare function selectCliRuntimeExternalDependencies( - dependencies: Readonly>, -): Record; -/** - * Scan an emitted bundle chunk for runtime-external packages that were inlined. - * - * Configuring the bundler is not the same as checking what it produced. The - * `alwaysBundle` predicate only forces packages IN; returning false from it - * means "no opinion", so a transitive dependency still gets bundled by default. - * msgpackr-extract, node-gyp-build-optional-packages and detect-libc were - * inlined that way while every list-based test passed, which is why this reads - * the artifact instead. - * - * `regionCount` is reported so the caller can tell "nothing was inlined" apart - * from "the marker format changed and this scan no longer sees anything". - * - * `inlinedPackages` is every package seen in a region, which lets the caller - * check the opposite direction too. Verifying only that externals are absent - * would still pass if the bundler reverted to leaving everything external: the - * scan would see source-file regions, report nothing inlined, and the packaged - * backends would then fail with ERR_MODULE_NOT_FOUND because those packages - * are not in the selected sidecar closure either. - */ -export declare function findInlinedExternalPackages(source: string): { - readonly regionCount: number; - readonly inlined: ReadonlyArray; - readonly inlinedPackages: ReadonlyArray; -}; diff --git a/scripts/lib/cli-external-packages.test.d.ts b/scripts/lib/cli-external-packages.test.d.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/scripts/lib/cli-external-packages.test.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/scripts/lib/dev-share.d.ts b/scripts/lib/dev-share.d.ts deleted file mode 100644 index 2ee05df72139..000000000000 --- a/scripts/lib/dev-share.d.ts +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Shares a running dev server on the local tailnet via `tailscale serve`, so it - * can be opened from a phone, another laptop, or by whoever is reviewing the - * work. - * - * Thin wrapper over `@t3tools/tailscale` (the same client the server's own - * `--tailscale-serve` uses). What it adds is dev-share semantics: replacing a - * stale mapping left by a killed run, and refusing to serve over routes it - * could not remove. - * - * Because browser dev is single-origin (Vite proxies the backend — see - * `resolveDevProxyTarget` in apps/web/vite.config.ts), one proxy rule covering - * the web port is enough; the backend needs no mapping of its own. - */ -import { type TailscaleCommandError } from "@t3tools/tailscale"; -import * as Effect from "effect/Effect"; -import * as Schema from "effect/Schema"; -import type { ChildProcessSpawner } from "effect/unstable/process"; -declare const TailscaleUnavailableError_base: Schema.Class< - TailscaleUnavailableError, - Schema.TaggedStruct< - "TailscaleUnavailableError", - { - readonly cause: Schema.Defect; - } - >, - import("effect/Cause").YieldableError ->; -/** - * Three distinct failures, three classes: each has its own caller-visible - * message and its own remedy, and `shareDevServer` chooses between them - * structurally. A single error with a `reason` discriminator would encode that - * distinction twice and put a lookup table in the `message` getter. - * - * Each wraps a real underlying failure and so keeps it as `cause`; the message - * is derived only from the structural fields, never from `cause.message`. - */ -export declare class TailscaleUnavailableError extends TailscaleUnavailableError_base { - get message(): string; - get hint(): string; -} -declare const TailnetNameMissingError_base: Schema.Class< - TailnetNameMissingError, - Schema.TaggedStruct<"TailnetNameMissingError", {}>, - import("effect/Cause").YieldableError ->; -/** No underlying failure: the status read succeeded and simply had no name. */ -export declare class TailnetNameMissingError extends TailnetNameMissingError_base { - get message(): string; - get hint(): string; -} -declare const DevServeFailedError_base: Schema.Class< - DevServeFailedError, - Schema.TaggedStruct< - "DevServeFailedError", - { - readonly stage: Schema.Literals; - readonly webPort: Schema.Number; - readonly explanation: Schema.optional; - readonly cause: Schema.optional; - } - >, - import("effect/Cause").YieldableError ->; -/** - * `stage` is a genuine multi-value discriminator: both stages share the same - * semantics (a `tailscale serve` invocation failed for this port) and differ - * only in which one, which the message states plainly. - */ -export declare class DevServeFailedError extends DevServeFailedError_base { - get message(): string; - get hint(): undefined; -} -export declare const DevShareError: Schema.Union< - readonly [ - typeof TailscaleUnavailableError, - typeof TailnetNameMissingError, - typeof DevServeFailedError, - ] ->; -export type DevShareError = typeof DevShareError.Type; -export declare const isDevShareError: ( - input: I, -) => input is I & (DevServeFailedError | TailnetNameMissingError | TailscaleUnavailableError); -/** - * Removes any mapping for `webPort`, reporting whether the port is now clear. - * - * Runs uninterruptibly: this is called from a finalizer on the way out of an - * interrupted program, and cancelling the cleanup subprocess would leave - * exactly the stale mapping it exists to remove. - */ -export declare const unshareDevServer: (webPort: number) => Effect.Effect< - { - readonly cleared: boolean; - readonly explanation?: string | undefined; - readonly cause?: TailscaleCommandError | undefined; - }, - never, - ChildProcessSpawner.ChildProcessSpawner ->; -export interface DevShareResult { - readonly url: string; - readonly host: string; -} -/** - * Publishes `webPort` on the tailnet at the same port number and returns the - * resulting HTTPS URL. Idempotent: re-running replaces any existing mapping. - */ -export declare const shareDevServer: (input: { readonly webPort: number }) => Effect.Effect< - { - url: string; - host: string; - }, - DevServeFailedError | TailnetNameMissingError | TailscaleUnavailableError, - ChildProcessSpawner.ChildProcessSpawner ->; -export {}; diff --git a/scripts/lib/dev-share.test.d.ts b/scripts/lib/dev-share.test.d.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/scripts/lib/dev-share.test.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/scripts/lib/icon-export.d.ts b/scripts/lib/icon-export.d.ts deleted file mode 100644 index c0084c016b5f..000000000000 --- a/scripts/lib/icon-export.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -export declare const WINDOWS_ICON_SIZES: readonly [16, 24, 32, 48, 64, 128, 256]; -export interface PngIconImage { - readonly size: number; - readonly contents: Buffer; -} -export declare function readPngDimensions(contents: Buffer): { - readonly width: number; - readonly height: number; -}; -/** Encodes PNG renditions directly into a modern, multi-resolution ICO file. */ -export declare function encodePngIco(images: ReadonlyArray): Buffer; diff --git a/scripts/lib/icon-export.test.d.ts b/scripts/lib/icon-export.test.d.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/scripts/lib/icon-export.test.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/scripts/lib/public-config.d.ts b/scripts/lib/public-config.d.ts deleted file mode 100644 index 8da6e580e662..000000000000 --- a/scripts/lib/public-config.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -export interface T3CodePublicConfig { - readonly clerkPublishableKey: string | undefined; - readonly clerkJwtTemplate: string | undefined; - readonly clerkCliOAuthClientId: string | undefined; - readonly relayUrl: string | undefined; - readonly mobileOtlpTracesUrl: string | undefined; - readonly mobileOtlpTracesDataset: string | undefined; - readonly mobileOtlpTracesToken: string | undefined; - readonly relayClientOtlpTracesUrl: string | undefined; - readonly relayClientOtlpTracesDataset: string | undefined; - readonly relayClientOtlpTracesToken: string | undefined; -} -type Environment = Readonly>; -export declare function loadRepoEnv({ - baseEnv, - repoRoot, -}?: { - readonly baseEnv?: Environment; - readonly repoRoot?: string; -}): Record; -export declare function resolvePublicConfig(...sources: readonly Environment[]): T3CodePublicConfig; -export {}; diff --git a/scripts/lib/public-config.test.d.ts b/scripts/lib/public-config.test.d.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/scripts/lib/public-config.test.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/scripts/lib/reference-repos.d.ts b/scripts/lib/reference-repos.d.ts deleted file mode 100644 index 063c678d777d..000000000000 --- a/scripts/lib/reference-repos.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface ReferenceRepo { - readonly id: string; - readonly prefix: string; - readonly repository: string; - readonly latestRef: string; - readonly versionSourcePath: string; - readonly packageVersionPath: ReadonlyArray; - readonly versionTagPrefix: string; -} -export declare const referenceRepos: ReadonlyArray; diff --git a/scripts/lib/resolve-catalog.d.ts b/scripts/lib/resolve-catalog.d.ts deleted file mode 100644 index 9303f3eee0d1..000000000000 --- a/scripts/lib/resolve-catalog.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Resolve `catalog:` dependency specs using the workspace catalog. - * - * Pure function: returns a new record with every `catalog:…` value replaced by - * the concrete version string found in `catalog`. Throws on missing entries. - */ -export declare function resolveCatalogDependencies( - dependencies: Record, - catalog: Record, - workspacePackage: string, -): Record; diff --git a/scripts/lib/update-manifest.d.ts b/scripts/lib/update-manifest.d.ts deleted file mode 100644 index fe27bac4f6fc..000000000000 --- a/scripts/lib/update-manifest.d.ts +++ /dev/null @@ -1,28 +0,0 @@ -export interface UpdateManifestFile { - readonly url: string; - readonly sha512: string; - readonly size: number; -} -export type UpdateManifestScalar = string | number | boolean; -export interface UpdateManifest { - readonly version: string; - readonly releaseDate: string; - readonly files: ReadonlyArray; - readonly extras: Readonly>; -} -export declare function parseUpdateManifest( - raw: string, - sourcePath: string, - platformLabel: string, -): UpdateManifest; -export declare function mergeUpdateManifests( - primary: UpdateManifest, - secondary: UpdateManifest, - platformLabel: string, -): UpdateManifest; -export declare function serializeUpdateManifest( - manifest: UpdateManifest, - options: { - readonly platformLabel: string; - }, -): string;