authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-07-31 00:06:49+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-31 00:06:49+02:00
logfbecb7365ede9892045dec4d03805d20ef9859f8
treeaf8de2f552b92d6ebde3ea611e91ad1e24ff2b92
parent4029b858eaaa3f029d30a02a930f887853239169
signature Signed by PGP key B5690EEEBB952194

fix(desktop): bump Clerk Electron SDK to 0.0.24 and register t3code:// scheme on Linux (#5015)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

13 files changed, 210 insertions(+), 95 deletions(-)

apps/desktop/src/app/DesktopAppIdentity.test.ts-1
......@@ -54,7 +54,6 @@ const makeElectronAppLayer = (calls: ElectronAppCalls) =>
5454 calls.setAboutPanelOptions.push(options);
5555 }),
5656 setAppUserModelId: () => Effect.void,
57 requestSingleInstanceLock: Effect.succeed(true),
5857 getAppMetrics: Effect.succeed([]),
5958 isDefaultProtocolClient: () => Effect.succeed(false),
6059 setAsDefaultProtocolClient: () => Effect.succeed(true),
apps/desktop/src/app/DesktopAppIdentity.ts+27-19
......@@ -45,6 +45,27 @@ const normalizeCommitHash = (value: string): Option.Option<string> => {
4545 : Option.none();
4646};
4747
48export const resolveUserDataPath = Effect.gen(function* () {
49 const environment = yield* DesktopEnvironment.DesktopEnvironment;
50 const fileSystem = yield* FileSystem.FileSystem;
51 const legacyPath = environment.path.join(
52 environment.appDataDirectory,
53 environment.legacyUserDataDirName,
54 );
55 const legacyPathExists = yield* fileSystem.exists(legacyPath).pipe(
56 Effect.mapError(
57 (cause) =>
58 new DesktopUserDataPathResolutionError({
59 legacyPath,
60 cause,
61 }),
62 ),
63 );
64 return legacyPathExists
65 ? legacyPath
66 : environment.path.join(environment.appDataDirectory, environment.userDataDirName);
67}).pipe(Effect.withSpan("desktop.appIdentity.resolveUserDataPath"));
68
4869export const make = Effect.gen(function* () {
4970 const assets = yield* DesktopAssets.DesktopAssets;
5071 const electronApp = yield* ElectronApp.ElectronApp;
......@@ -90,24 +111,11 @@ export const make = Effect.gen(function* () {
90111 return commitHash;
91112 });
92113
93 const resolveUserDataPath = Effect.gen(function* () {
94 const legacyPath = environment.path.join(
95 environment.appDataDirectory,
96 environment.legacyUserDataDirName,
97 );
98 const legacyPathExists = yield* fileSystem.exists(legacyPath).pipe(
99 Effect.mapError(
100 (cause) =>
101 new DesktopUserDataPathResolutionError({
102 legacyPath,
103 cause,
104 }),
105 ),
106 );
107 return legacyPathExists
108 ? legacyPath
109 : environment.path.join(environment.appDataDirectory, environment.userDataDirName);
110 }).pipe(Effect.withSpan("desktop.appIdentity.resolveUserDataPath"));
114 const userDataPath = resolveUserDataPath.pipe(
115 Effect.provide(
116 yield* Effect.context<DesktopEnvironment.DesktopEnvironment | FileSystem.FileSystem>(),
117 ),
118 );
111119
112120 const configure = Effect.gen(function* () {
113121 const commitHash = yield* resolveAboutCommitHash;
......@@ -136,7 +144,7 @@ export const make = Effect.gen(function* () {
136144 }).pipe(Effect.withSpan("desktop.appIdentity.configure"));
137145
138146 return DesktopAppIdentity.of({
139 resolveUserDataPath,
147 resolveUserDataPath: userDataPath,
140148 configure,
141149 });
142150});
apps/desktop/src/app/DesktopClerk.test.ts+90-5
......@@ -22,17 +22,38 @@ vi.mock("@clerk/electron/storage", () => ({
2222 storage: storageMock,
2323}));
2424
25import * as Exit from "effect/Exit";
26import * as FileSystem from "effect/FileSystem";
27import * as ElectronApp from "../electron/ElectronApp.ts";
28import * as ElectronWindow from "../electron/ElectronWindow.ts";
2529import * as DesktopClerk from "./DesktopClerk.ts";
2630import * as DesktopEnvironment from "./DesktopEnvironment.ts";
2731
28const makeDesktopClerkLayer = (isDevelopment = true) => {
32const makeDesktopClerkLayer = (isDevelopment = true, events: string[] = []) => {
2933 const environment = DesktopEnvironment.DesktopEnvironment.of({
3034 stateDir: "/tmp/t3-state",
3135 isDevelopment,
36 appDataDirectory: "/tmp/app-data",
37 userDataDirName: isDevelopment ? "t3code-dev" : "t3code",
38 legacyUserDataDirName: isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)",
39 path: { join: (...parts: ReadonlyArray<string>) => parts.join("/") },
3240 } as unknown as DesktopEnvironment.DesktopEnvironment["Service"]);
3341
42 const electronApp = {
43 setPath: (name: string, value: string) =>
44 Effect.sync(() => {
45 events.push(`setPath:${name}:${value}`);
46 }),
47 } as unknown as ElectronApp.ElectronApp["Service"];
48
3449 return DesktopClerk.layer.pipe(
35 Layer.provide(Layer.succeed(DesktopEnvironment.DesktopEnvironment, environment)),
50 Layer.provide(
51 Layer.mergeAll(
52 Layer.succeed(DesktopEnvironment.DesktopEnvironment, environment),
53 Layer.succeed(ElectronApp.ElectronApp, electronApp),
54 FileSystem.layerNoop({ exists: () => Effect.succeed(false) }),
55 ),
56 ),
3657 );
3758};
3859
......@@ -55,11 +76,15 @@ describe("DesktopClerk", () => {
5576
5677 it.effect("acquires and releases the SDK bridge with the layer", () => {
5778 const cleanup = vi.fn();
79 const events: string[] = [];
5880 storageMock.mockReturnValue(storageAdapter);
59 createClerkBridgeMock.mockReturnValue({ cleanup });
81 createClerkBridgeMock.mockImplementation(() => {
82 events.push("createClerkBridge");
83 return { cleanup, isPrimaryInstance: true };
84 });
6085
6186 return Effect.gen(function* () {
62 yield* Effect.scoped(Layer.build(makeDesktopClerkLayer()));
87 yield* Effect.scoped(Layer.build(makeDesktopClerkLayer(true, events)));
6388
6489 assert.deepEqual(createClerkBridgeMock.mock.calls, [
6590 [
......@@ -71,6 +96,10 @@ describe("DesktopClerk", () => {
7196 ],
7297 ]);
7398 assert.equal(cleanup.mock.calls.length, 1);
99 // The bridge acquires Electron's single-instance lock at creation, and
100 // the lock both lives in and creates the userData directory — so the
101 // real path must be set before the bridge exists.
102 assert.deepEqual(events, ["setPath:userData:/tmp/app-data/t3code-dev", "createClerkBridge"]);
74103 storageMock.mockClear();
75104 createClerkBridgeMock.mockClear();
76105 });
......@@ -124,11 +153,67 @@ describe("DesktopClerk", () => {
124153 });
125154 });
126155
156 it.effect("registers the second-instance handler in the primary instance", () => {
157 storageMock.mockReturnValue(storageAdapter);
158 createClerkBridgeMock.mockReturnValue({ cleanup: vi.fn(), isPrimaryInstance: true });
159 const quit = vi.fn();
160 const registeredEvents: string[] = [];
161 const electronApp = {
162 quit: Effect.sync(quit),
163 on: (eventName: string) =>
164 Effect.sync(() => {
165 registeredEvents.push(eventName);
166 }),
167 } as unknown as ElectronApp.ElectronApp["Service"];
168 const electronWindow = {} as ElectronWindow.ElectronWindow["Service"];
169
170 return Effect.gen(function* () {
171 const clerk = yield* DesktopClerk.DesktopClerk;
172 const exit = yield* Effect.exit(Effect.scoped(clerk.configure));
173
174 assert.isTrue(Exit.isSuccess(exit));
175 assert.equal(quit.mock.calls.length, 0);
176 assert.deepEqual(registeredEvents, ["second-instance"]);
177 }).pipe(
178 Effect.provide(makeDesktopClerkLayer()),
179 Effect.provideService(ElectronApp.ElectronApp, electronApp),
180 Effect.provideService(ElectronWindow.ElectronWindow, electronWindow),
181 );
182 });
183
184 it.effect("quits and interrupts startup in a secondary instance", () => {
185 storageMock.mockReturnValue(storageAdapter);
186 createClerkBridgeMock.mockReturnValue({ cleanup: vi.fn(), isPrimaryInstance: false });
187 const quit = vi.fn();
188 const registeredEvents: string[] = [];
189 const electronApp = {
190 quit: Effect.sync(quit),
191 on: (eventName: string) =>
192 Effect.sync(() => {
193 registeredEvents.push(eventName);
194 }),
195 } as unknown as ElectronApp.ElectronApp["Service"];
196 const electronWindow = {} as ElectronWindow.ElectronWindow["Service"];
197
198 return Effect.gen(function* () {
199 const clerk = yield* DesktopClerk.DesktopClerk;
200 const exit = yield* Effect.exit(Effect.scoped(clerk.configure));
201
202 assert.isTrue(Exit.hasInterrupts(exit));
203 assert.equal(quit.mock.calls.length, 1);
204 assert.deepEqual(registeredEvents, []);
205 }).pipe(
206 Effect.provide(makeDesktopClerkLayer()),
207 Effect.provideService(ElectronApp.ElectronApp, electronApp),
208 Effect.provideService(ElectronWindow.ElectronWindow, electronWindow),
209 );
210 });
211
127212 it.each([
128213 { isDevelopment: true, scheme: "t3code-dev" },
129214 { isDevelopment: false, scheme: "t3code" },
130215 ])("configures the SDK with the $scheme renderer origin", ({ isDevelopment, scheme }) => {
131 const bridge = { cleanup: vi.fn() };
216 const bridge = { cleanup: vi.fn(), isPrimaryInstance: true };
132217 storageMock.mockReturnValue(storageAdapter);
133218 createClerkBridgeMock.mockReturnValue(bridge);
134219
apps/desktop/src/app/DesktopClerk.ts+19-2
......@@ -11,6 +11,7 @@ import { clerkFrontendApiHostnameFromPublishableKey } from "@t3tools/shared/rela
1111import * as ElectronApp from "../electron/ElectronApp.ts";
1212import * as ElectronProtocol from "../electron/ElectronProtocol.ts";
1313import * as ElectronWindow from "../electron/ElectronWindow.ts";
14import * as DesktopAppIdentity from "./DesktopAppIdentity.ts";
1415import * as DesktopEnvironment from "./DesktopEnvironment.ts";
1516
1617declare const __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__: string | undefined;
......@@ -84,7 +85,18 @@ export function createDesktopClerkBridge(stateDir: string, isDevelopment: boolea
8485
8586export const make = Effect.gen(function* () {
8687 const environment = yield* DesktopEnvironment.DesktopEnvironment;
87 yield* Effect.acquireRelease(
88 const electronApp = yield* ElectronApp.ElectronApp;
89
90 // Electron scopes the single-instance lock to the userData directory and
91 // creates that directory when the lock is acquired. The SDK bridge takes
92 // the lock at creation, so userData must already point at the real
93 // directory here — under the default productName-derived path, acquiring
94 // the lock would create "T3 Code (Alpha)" and make the legacy-install
95 // detection in resolveUserDataPath match on fresh installs.
96 const userDataPath = yield* DesktopAppIdentity.resolveUserDataPath;
97 yield* electronApp.setPath("userData", userDataPath);
98
99 const bridge = yield* Effect.acquireRelease(
88100 Effect.try({
89101 try: () => createDesktopClerkBridge(environment.stateDir, environment.isDevelopment),
90102 catch: (cause) =>
......@@ -113,7 +125,12 @@ export const make = Effect.gen(function* () {
113125 const context = yield* Effect.context<ElectronWindow.ElectronWindow>();
114126 const runPromise = Effect.runPromiseWith(context);
115127
116 if (!(yield* electronApp.requestSingleInstanceLock)) {
128 // The SDK bridge holds Electron's single-instance lock (acquired at
129 // bridge creation) so OAuth deep-link callbacks on Windows/Linux are
130 // forwarded to the running app. In a secondary instance the bridge has
131 // already begun quitting the app; app.quit() is asynchronous, so stop
132 // bootstrap here before whenReady can fire.
133 if (!bridge.isPrimaryInstance) {
117134 yield* electronApp.quit;
118135 return yield* Effect.interrupt;
119136 }
apps/desktop/src/app/DesktopLifecycle.test.ts-1
......@@ -29,7 +29,6 @@ describe("DesktopLifecycle", () => {
2929 setName: () => Effect.void,
3030 setAboutPanelOptions: () => Effect.void,
3131 setAppUserModelId: () => Effect.void,
32 requestSingleInstanceLock: Effect.succeed(true),
3332 getAppMetrics: Effect.succeed([]),
3433 isDefaultProtocolClient: () => Effect.succeed(false),
3534 setAsDefaultProtocolClient: () => Effect.succeed(true),
apps/desktop/src/electron/ElectronApp.test.ts-3
......@@ -14,7 +14,6 @@ const {
1414 quitMock,
1515 relaunchMock,
1616 removeListenerMock,
17 requestSingleInstanceLockMock,
1817 setAboutPanelOptionsMock,
1918 setAppUserModelIdMock,
2019 setAsDefaultProtocolClientMock,
......@@ -35,7 +34,6 @@ const {
3534 quitMock: vi.fn(),
3635 relaunchMock: vi.fn(),
3736 removeListenerMock: vi.fn(),
38 requestSingleInstanceLockMock: vi.fn(() => true),
3937 setAboutPanelOptionsMock: vi.fn(),
4038 setAppUserModelIdMock: vi.fn(),
4139 setAsDefaultProtocolClientMock: vi.fn(() => true),
......@@ -67,7 +65,6 @@ vi.mock("electron", () => ({
6765 quit: quitMock,
6866 relaunch: relaunchMock,
6967 removeListener: removeListenerMock,
70 requestSingleInstanceLock: requestSingleInstanceLockMock,
7168 runningUnderARM64Translation: false,
7269 setAboutPanelOptions: setAboutPanelOptionsMock,
7370 setAsDefaultProtocolClient: setAsDefaultProtocolClientMock,
apps/desktop/src/electron/ElectronApp.ts-2
......@@ -56,7 +56,6 @@ export class ElectronApp extends Context.Service<
5656 options: Electron.AboutPanelOptionsOptions,
5757 ) => Effect.Effect<void>;
5858 readonly setAppUserModelId: (id: string) => Effect.Effect<void>;
59 readonly requestSingleInstanceLock: Effect.Effect<boolean>;
6059 readonly getAppMetrics: Effect.Effect<ReadonlyArray<Electron.ProcessMetric>>;
6160 readonly isDefaultProtocolClient: (protocol: string) => Effect.Effect<boolean>;
6261 readonly setAsDefaultProtocolClient: (
......@@ -153,7 +152,6 @@ export const make = ElectronApp.of({
153152 Effect.sync(() => {
154153 Electron.app.setAppUserModelId(id);
155154 }),
156 requestSingleInstanceLock: Effect.sync(() => Electron.app.requestSingleInstanceLock()),
157155 getAppMetrics: Effect.sync(() => Electron.app.getAppMetrics()),
158156 isDefaultProtocolClient: (protocol) =>
159157 Effect.sync(() => Electron.app.isDefaultProtocolClient(protocol)),
apps/desktop/src/telemetry/DesktopTelemetryPublisher.test.ts-1
......@@ -34,7 +34,6 @@ function makeElectronAppLayer(
3434 setName: () => Effect.void,
3535 setAboutPanelOptions: () => Effect.void,
3636 setAppUserModelId: () => Effect.void,
37 requestSingleInstanceLock: Effect.succeed(true),
3837 getAppMetrics: Effect.sync(() => {
3938 onMetricsRead();
4039 return metrics;
apps/desktop/src/window/DesktopApplicationMenu.test.ts-1
......@@ -39,7 +39,6 @@ const electronAppLayer = Layer.succeed(ElectronApp.ElectronApp, {
3939 setName: () => Effect.void,
4040 setAboutPanelOptions: () => Effect.void,
4141 setAppUserModelId: () => Effect.void,
42 requestSingleInstanceLock: Effect.succeed(true),
4342 getAppMetrics: Effect.succeed([]),
4443 isDefaultProtocolClient: () => Effect.succeed(false),
4544 setAsDefaultProtocolClient: () => Effect.succeed(true),
pnpm-lock.yaml+48-48
......@@ -35,19 +35,19 @@ catalogs:
3535 version: 0.2.2
3636
3737overrides:
38 '@clerk/backend': 3.13.0
39 '@clerk/clerk-js': 6.25.7
38 '@clerk/backend': 3.14.0
39 '@clerk/clerk-js': 6.25.12
4040 '@clerk/clerk-js>@base-org/account': '-'
4141 '@clerk/clerk-js>@coinbase/wallet-sdk': '-'
4242 '@clerk/clerk-js>@solana/wallet-adapter-base': '-'
4343 '@clerk/clerk-js>@solana/wallet-adapter-react': '-'
4444 '@clerk/clerk-js>@solana/wallet-standard': '-'
4545 '@clerk/clerk-js>@wallet-standard/core': '-'
46 '@clerk/electron': 0.0.18
46 '@clerk/electron': 0.0.24
4747 '@clerk/electron-passkeys': 0.0.3
48 '@clerk/expo': 4.0.2
49 '@clerk/react': 6.12.7
50 '@clerk/shared': 4.25.7
48 '@clerk/expo': 4.1.2
49 '@clerk/react': 6.12.9
50 '@clerk/shared': 4.25.9
5151 '@effect/atom-react': 4.0.0-beta.102
5252 '@effect/platform-bun': 4.0.0-beta.102
5353 '@effect/platform-node': 4.0.0-beta.102
......@@ -111,8 +111,8 @@ importers:
111111 apps/desktop:
112112 dependencies:
113113 '@clerk/electron':
114 specifier: 0.0.18
115 version: 0.0.18(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@41.5.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
114 specifier: 0.0.24
115 version: 0.0.24(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@41.5.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
116116 '@clerk/electron-passkeys':
117117 specifier: 0.0.3
118118 version: 0.0.3
......@@ -197,8 +197,8 @@ importers:
197197 specifier: ^0.7.1
198198 version: 0.7.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
199199 '@clerk/expo':
200 specifier: 4.0.2
201 version: 4.0.2(expo-auth-session@56.0.14(expo@56.0.12)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(expo-constants@56.0.18)(expo-crypto@56.0.4(expo@56.0.12))(expo-secure-store@56.0.4(expo@56.0.12))(expo-web-browser@56.0.5(expo@56.0.12)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@56.0.12)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)
200 specifier: 4.1.2
201 version: 4.1.2(expo-auth-session@56.0.14(expo@56.0.12)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(expo-constants@56.0.18)(expo-crypto@56.0.4(expo@56.0.12))(expo-secure-store@56.0.4(expo@56.0.12))(expo-web-browser@56.0.5(expo@56.0.12)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@56.0.12)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)
202202 '@effect/atom-react':
203203 specifier: 4.0.0-beta.102
204204 version: 4.0.0-beta.102(effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488))(react@19.2.3)(scheduler@0.27.0)
......@@ -514,11 +514,11 @@ importers:
514514 specifier: ^1.4.1
515515 version: 1.5.0(@types/react@19.2.16)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
516516 '@clerk/electron':
517 specifier: 0.0.18
518 version: 0.0.18(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@41.5.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
517 specifier: 0.0.24
518 version: 0.0.24(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@41.5.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
519519 '@clerk/react':
520 specifier: 6.12.7
521 version: 6.12.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
520 specifier: 6.12.9
521 version: 6.12.9(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
522522 '@dnd-kit/core':
523523 specifier: ^6.3.1
524524 version: 6.3.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
......@@ -668,8 +668,8 @@ importers:
668668 infra/relay:
669669 dependencies:
670670 '@clerk/backend':
671 specifier: 3.13.0
672 version: 3.13.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
671 specifier: 3.14.0
672 version: 3.14.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
673673 '@effect/sql-pg':
674674 specifier: 4.0.0-beta.102
675675 version: 4.0.0-beta.102(effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488))
......@@ -1695,12 +1695,12 @@ packages:
16951695 resolution: {integrity: sha512-EYlRokl8szrP9Z25qT5aepMdBjzBvHF9ZEhzIiUBc9guz/T31EqRgvD0QSgZcpE93xiwrr+OkB4nz0BZyF6fSA==}
16961696 engines: {node: '>= 20.12.0'}
16971697
1698 '@clerk/backend@3.13.0':
1699 resolution: {integrity: sha512-kYOgeR48iXYypudpYITE0L2yxi4TeFXU+ISAOULlVgOcurw8qnm1/JEuK6Mr2IFUbRS9hcHk1HVO0Ceo0XmL8Q==}
1698 '@clerk/backend@3.14.0':
1699 resolution: {integrity: sha512-WsphTvDFHDuQilKI7dyVE5qmt7USu8qSrujAq6SqpEHbVFfNBfINDNuzxlF9M2skOTfHFfgiTE8Jjb1egIBGLg==}
17001700 engines: {node: '>=20.9.0'}
17011701
1702 '@clerk/clerk-js@6.25.7':
1703 resolution: {integrity: sha512-GOnbEUzPKAdsdCodZsHhku2YloBBuwLGx5UP0VeezUj73wKLwlSYMlVPuntsJoY7c8+lVrMCY9LQWYVZ7VMLBw==}
1702 '@clerk/clerk-js@6.25.12':
1703 resolution: {integrity: sha512-N91mFKenMF5e+LRQ0NsqFb8bw+icbwnF8MzjzS4cux72xG4Gekh772tiLIetbURe44Srgjw5lj8jkWbt+yH/FA==}
17041704 engines: {node: '>=20.9.0'}
17051705
17061706 '@clerk/electron-passkeys-darwin-arm64@0.0.3':
......@@ -1727,8 +1727,8 @@ packages:
17271727 resolution: {integrity: sha512-OHhIe88qDL+FxyBalXdXNHAS5eEramr6Rerp+6iNkfkjqT8rx4hHNmfpmjg5/T1/am8QfknbOBZkqoXZlCrjPg==}
17281728 engines: {node: '>=20.9.0'}
17291729
1730 '@clerk/electron@0.0.18':
1731 resolution: {integrity: sha512-0lWiITlTxCviglPfPSu+uhERnQyXroiWrSMRpZcEO3FgBt3bnrpfgZUrXW8iaLoUYJrFP2byF7bHJPRwEPVXEw==}
1730 '@clerk/electron@0.0.24':
1731 resolution: {integrity: sha512-xORO51KLzCqmRQ4IVsa+Amp1M5NjG28QnvsaRBq1v4CUEjH8vAXDKCPiPBKAE+HuQB6HUhzYmzUkyTw2FcDonw==}
17321732 engines: {node: '>=20.9.0'}
17331733 peerDependencies:
17341734 '@clerk/electron-passkeys': 0.0.3
......@@ -1744,8 +1744,8 @@ packages:
17441744 react-dom:
17451745 optional: true
17461746
1747 '@clerk/expo@4.0.2':
1748 resolution: {integrity: sha512-afBdV2bLRM55p5L7FoAGV/wCamLxXusjIBPmNo+TwAYabU0oUokMsm+0++xWDA0nxzMt/O08Oj+xAGEtE+Qvxg==}
1747 '@clerk/expo@4.1.2':
1748 resolution: {integrity: sha512-ZyGozbbizcGwEyK0CEXsPHnHnQoaa2CbTsXAqWBygzKEZyFjLjCHwQsQBKj6WgjAgn8mtw2ByIP9qLpItZffgw==}
17491749 engines: {node: '>=20.9.0'}
17501750 peerDependencies:
17511751 '@clerk/expo-google-signin': '>=0.1.0'
......@@ -1783,15 +1783,15 @@ packages:
17831783 react-dom:
17841784 optional: true
17851785
1786 '@clerk/react@6.12.7':
1787 resolution: {integrity: sha512-K8CK0tS7My/3RT5sAIJTjYgnH0xU+K4Jdnhi76xQdAY4agD/M07BbrBMWXEE0xUEDst2PqPfX660V1LRLOsxOQ==}
1786 '@clerk/react@6.12.9':
1787 resolution: {integrity: sha512-lQ1UOJhHGDweLCDf4IgT5r3rgkch1Gx21hNqK2/kOohJrJd/XM89fq2F7VK9Kg/Byc14TsfCh64Dm8ej2MyRWw==}
17881788 engines: {node: '>=20.9.0'}
17891789 peerDependencies:
17901790 react: ^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0
17911791 react-dom: ^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0
17921792
1793 '@clerk/shared@4.25.7':
1794 resolution: {integrity: sha512-FfBbDeFkxwDuz/2YcVs4DhyZ0VxnqNtdbO0Tn/lkLAiDvFFspWw+PDYqo7LQiBymEQaOQkb094dX21WWnZbmlA==}
1793 '@clerk/shared@4.25.9':
1794 resolution: {integrity: sha512-nw3maGzqPrwmUKjGffOM9Zvw24IyOllq0TkQGzsTfQX312WLu2u5CNvjKntMb6Ok6kXa1Zdc77zmPLn1/EAIFA==}
17951795 engines: {node: '>=20.9.0'}
17961796 peerDependencies:
17971797 react: ^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0
......@@ -11512,18 +11512,18 @@ snapshots:
1151211512 fast-wrap-ansi: 0.2.2
1151311513 sisteransi: 1.0.5
1151411514
11515 '@clerk/backend@3.13.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
11515 '@clerk/backend@3.14.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
1151611516 dependencies:
11517 '@clerk/shared': 4.25.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
11517 '@clerk/shared': 4.25.9(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
1151811518 standardwebhooks: 1.0.0
1151911519 tslib: 2.8.1
1152011520 transitivePeerDependencies:
1152111521 - react
1152211522 - react-dom
1152311523
11524 '@clerk/clerk-js@6.25.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
11524 '@clerk/clerk-js@6.25.12(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
1152511525 dependencies:
11526 '@clerk/shared': 4.25.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
11526 '@clerk/shared': 4.25.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
1152711527 '@stripe/stripe-js': 5.6.0
1152811528 '@swc/helpers': 0.5.21
1152911529 '@tanstack/query-core': 5.100.14
......@@ -11538,9 +11538,9 @@ snapshots:
1153811538 - react
1153911539 - react-dom
1154011540
11541 '@clerk/clerk-js@6.25.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
11541 '@clerk/clerk-js@6.25.12(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
1154211542 dependencies:
11543 '@clerk/shared': 4.25.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
11543 '@clerk/shared': 4.25.9(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
1154411544 '@stripe/stripe-js': 5.6.0
1154511545 '@swc/helpers': 0.5.21
1154611546 '@tanstack/query-core': 5.100.14
......@@ -11574,11 +11574,11 @@ snapshots:
1157411574 '@clerk/electron-passkeys-win32-arm64-msvc': 0.0.3
1157511575 '@clerk/electron-passkeys-win32-x64-msvc': 0.0.3
1157611576
11577 '@clerk/electron@0.0.18(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@41.5.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
11577 '@clerk/electron@0.0.24(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@41.5.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
1157811578 dependencies:
11579 '@clerk/clerk-js': 6.25.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
11580 '@clerk/react': 6.12.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
11581 '@clerk/shared': 4.25.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
11579 '@clerk/clerk-js': 6.25.12(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
11580 '@clerk/react': 6.12.9(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
11581 '@clerk/shared': 4.25.9(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
1158211582 electron: 41.5.0
1158311583 react: 19.2.6
1158411584 tslib: 2.8.1
......@@ -11587,11 +11587,11 @@ snapshots:
1158711587 electron-store: 8.2.0
1158811588 react-dom: 19.2.6(react@19.2.6)
1158911589
11590 '@clerk/expo@4.0.2(expo-auth-session@56.0.14(expo@56.0.12)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(expo-constants@56.0.18)(expo-crypto@56.0.4(expo@56.0.12))(expo-secure-store@56.0.4(expo@56.0.12))(expo-web-browser@56.0.5(expo@56.0.12)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@56.0.12)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)':
11590 '@clerk/expo@4.1.2(expo-auth-session@56.0.14(expo@56.0.12)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(expo-constants@56.0.18)(expo-crypto@56.0.4(expo@56.0.12))(expo-secure-store@56.0.4(expo@56.0.12))(expo-web-browser@56.0.5(expo@56.0.12)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@56.0.12)(react-dom@19.2.3(react@19.2.3))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3)':
1159111591 dependencies:
11592 '@clerk/clerk-js': 6.25.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
11593 '@clerk/react': 6.12.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
11594 '@clerk/shared': 4.25.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
11592 '@clerk/clerk-js': 6.25.12(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
11593 '@clerk/react': 6.12.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
11594 '@clerk/shared': 4.25.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
1159511595 '@expo/config-plugins': 56.0.9(typescript@6.0.3)
1159611596 base-64: 1.0.0
1159711597 expo: 56.0.12(8895228379997a2a064f9644cda56ed0)
......@@ -11610,21 +11610,21 @@ snapshots:
1161011610 - supports-color
1161111611 - typescript
1161211612
11613 '@clerk/react@6.12.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
11613 '@clerk/react@6.12.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
1161411614 dependencies:
11615 '@clerk/shared': 4.25.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
11615 '@clerk/shared': 4.25.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
1161611616 react: 19.2.3
1161711617 react-dom: 19.2.3(react@19.2.3)
1161811618 tslib: 2.8.1
1161911619
11620 '@clerk/react@6.12.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
11620 '@clerk/react@6.12.9(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
1162111621 dependencies:
11622 '@clerk/shared': 4.25.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
11622 '@clerk/shared': 4.25.9(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
1162311623 react: 19.2.6
1162411624 react-dom: 19.2.6(react@19.2.6)
1162511625 tslib: 2.8.1
1162611626
11627 '@clerk/shared@4.25.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
11627 '@clerk/shared@4.25.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
1162811628 dependencies:
1162911629 '@tanstack/query-core': 5.100.14
1163011630 dequal: 2.0.3
......@@ -11634,7 +11634,7 @@ snapshots:
1163411634 react: 19.2.3
1163511635 react-dom: 19.2.3(react@19.2.3)
1163611636
11637 '@clerk/shared@4.25.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
11637 '@clerk/shared@4.25.9(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
1163811638 dependencies:
1163911639 '@tanstack/query-core': 5.100.14
1164011640 dequal: 2.0.3
pnpm-workspace.yaml+12-12
......@@ -23,13 +23,13 @@ allowBuilds:
2323 workerd: false
2424
2525catalog:
26 "@clerk/backend": 3.13.0
27 "@clerk/clerk-js": 6.25.7
28 "@clerk/electron": 0.0.18
26 "@clerk/backend": 3.14.0
27 "@clerk/clerk-js": 6.25.12
28 "@clerk/electron": 0.0.24
2929 "@clerk/electron-passkeys": 0.0.3
30 "@clerk/expo": 4.0.2
31 "@clerk/react": 6.12.7
32 "@clerk/shared": 4.25.7
30 "@clerk/expo": 4.1.2
31 "@clerk/react": 6.12.9
32 "@clerk/shared": 4.25.9
3333 "@effect/atom-react": 4.0.0-beta.102
3434 "@effect/openapi-generator": 4.0.0-beta.102
3535 "@effect/platform-bun": 4.0.0-beta.102
......@@ -52,12 +52,12 @@ catalog:
5252 yaml: ^2.9.0
5353
5454minimumReleaseAgeExclude:
55 - "@clerk/backend@3.13.0"
56 - "@clerk/clerk-js@6.25.7"
57 - "@clerk/electron@0.0.18"
58 - "@clerk/expo@4.0.2"
59 - "@clerk/react@6.12.7"
60 - "@clerk/shared@4.25.7"
55 - "@clerk/backend@3.14.0"
56 - "@clerk/clerk-js@6.25.12"
57 - "@clerk/electron@0.0.24"
58 - "@clerk/expo@4.1.2"
59 - "@clerk/react@6.12.9"
60 - "@clerk/shared@4.25.9"
6161 - "@distilled.cloud/aws@0.30.2"
6262 - "@distilled.cloud/axiom@0.30.2"
6363 - "@distilled.cloud/cloudflare@0.30.2"
scripts/build-desktop-artifact.test.ts+5
......@@ -349,6 +349,11 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
349349 assert.notProperty(mac, "asarUnpack");
350350 assert.notProperty(linux, "asarUnpack");
351351 assert.deepStrictEqual(win.asarUnpack, WINDOWS_ASAR_UNPACK);
352 // Linux must register the renderer schemes so the generated .desktop
353 // entry advertises MimeType=x-scheme-handler/t3code; for OAuth deep links.
354 assert.deepStrictEqual((linux.linux as Record<string, unknown>).protocols, [
355 { name: "T3 Code", schemes: ["t3code", "t3code-dev"] },
356 ]);
352357 for (const config of [mac, linux, win]) {
353358 assert.deepStrictEqual(config.electronLanguages, DESKTOP_ELECTRON_LANGUAGES);
354359 assert.deepStrictEqual(config.files, DESKTOP_FILE_EXCLUSIONS);
scripts/build-desktop-artifact.ts+9
......@@ -1589,6 +1589,15 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* (
15891589 executableName: "t3code",
15901590 icon: "icons",
15911591 category: "Development",
1592 // electron-builder turns these into MimeType=x-scheme-handler/<scheme>;
1593 // in the .desktop entry (Exec already gets %U), so browsers can hand
1594 // t3code:// OAuth callbacks to the app.
1595 protocols: [
1596 {
1597 name: "T3 Code",
1598 schemes: ["t3code", "t3code-dev"],
1599 },
1600 ],
15921601 desktop: {
15931602 entry: {
15941603 StartupWMClass: "t3code",