aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Florian Lefebvre <contact@florian-lefebvre.dev> 2025-06-05 16:18:32 +0200
committerGravatar Florian Lefebvre <contact@florian-lefebvre.dev> 2025-06-05 16:18:32 +0200
commit61e779486fffd77eed4b95eec0ab5fcf106dd2d5 (patch)
tree85518de42eea4004e4c891676bce52836de06461
parent0947a69192ad6820970902c7c951fb0cf31fcf4b (diff)
downloadastro-feat/remove-studio.tar.gz
astro-feat/remove-studio.tar.zst
astro-feat/remove-studio.zip
feat: remove studiofeat/remove-studio
-rw-r--r--packages/astro/src/cli/index.ts5
-rw-r--r--packages/astro/test/fixtures/ssr-prerender-chunks/deps/test-adapter/server.js6
-rw-r--r--packages/db/package.json3
-rw-r--r--packages/db/src/core/cli/commands/execute/index.ts7
-rw-r--r--packages/db/src/core/cli/commands/link/index.ts295
-rw-r--r--packages/db/src/core/cli/commands/login/index.ts96
-rw-r--r--packages/db/src/core/cli/commands/logout/index.ts7
-rw-r--r--packages/db/src/core/cli/commands/push/index.ts40
-rw-r--r--packages/db/src/core/cli/commands/shell/index.ts4
-rw-r--r--packages/db/src/core/cli/commands/verify/index.ts4
-rw-r--r--packages/db/src/core/cli/index.ts20
-rw-r--r--packages/db/src/core/cli/migration-queries.ts38
-rw-r--r--packages/db/src/core/integration/index.ts12
-rw-r--r--packages/db/src/core/integration/vite-plugin-db.ts19
-rw-r--r--packages/db/src/core/load-file.ts2
-rw-r--r--packages/db/src/core/schemas.ts2
-rw-r--r--packages/db/src/core/utils.ts37
-rw-r--r--packages/db/src/runtime/db-client.ts183
-rw-r--r--packages/db/test/basics.test.js4
-rw-r--r--packages/db/test/ssr-no-apptoken.test.js2
-rw-r--r--packages/db/test/test-utils.js8
-rw-r--r--packages/db/test/unit/remote-info.test.js50
-rw-r--r--packages/integrations/cloudflare/src/entrypoints/server.ts7
-rw-r--r--packages/studio/CHANGELOG.md69
-rw-r--r--packages/studio/README.md34
-rw-r--r--packages/studio/package.json46
-rw-r--r--packages/studio/src/core/errors.ts15
-rw-r--r--packages/studio/src/core/tokens.ts225
-rw-r--r--packages/studio/src/core/utils.ts11
-rw-r--r--packages/studio/src/index.ts3
-rw-r--r--packages/studio/tsconfig.json7
-rw-r--r--pnpm-lock.yaml26
32 files changed, 50 insertions, 1237 deletions
diff --git a/packages/astro/src/cli/index.ts b/packages/astro/src/cli/index.ts
index 8511a8304..94c08d7b0 100644
--- a/packages/astro/src/cli/index.ts
+++ b/packages/astro/src/cli/index.ts
@@ -40,11 +40,6 @@ async function printAstroHelp() {
['preferences', 'Configure user preferences.'],
['telemetry', 'Configure telemetry settings.'],
],
- 'Studio Commands': [
- ['login', 'Authenticate your machine with Astro Studio.'],
- ['logout', 'End your authenticated session with Astro Studio.'],
- ['link', 'Link this project directory to an Astro Studio project.'],
- ],
'Global Flags': [
['--config <path>', 'Specify your config file.'],
['--root <path>', 'Specify your project root folder.'],
diff --git a/packages/astro/test/fixtures/ssr-prerender-chunks/deps/test-adapter/server.js b/packages/astro/test/fixtures/ssr-prerender-chunks/deps/test-adapter/server.js
index 0921fe782..0515aaaa1 100644
--- a/packages/astro/test/fixtures/ssr-prerender-chunks/deps/test-adapter/server.js
+++ b/packages/astro/test/fixtures/ssr-prerender-chunks/deps/test-adapter/server.js
@@ -32,12 +32,6 @@ import { App } from 'astro/app';
request.headers.get('cf-connecting-ip')
);
- process.env.ASTRO_STUDIO_APP_TOKEN ??= (() => {
- if (typeof env.ASTRO_STUDIO_APP_TOKEN === 'string') {
- return env.ASTRO_STUDIO_APP_TOKEN;
- }
- })();
-
const locals = {
runtime: {
env: env,
diff --git a/packages/db/package.json b/packages/db/package.json
index 5d0b25efd..a10435695 100644
--- a/packages/db/package.json
+++ b/packages/db/package.json
@@ -1,7 +1,7 @@
{
"name": "@astrojs/db",
"version": "0.15.0",
- "description": "Add libSQL and Astro Studio support to your Astro site",
+ "description": "Add libSQL support to your Astro site",
"license": "MIT",
"repository": {
"type": "git",
@@ -69,7 +69,6 @@
"test": "astro-scripts test \"test/**/*.test.js\""
},
"dependencies": {
- "@astrojs/studio": "workspace:*",
"@libsql/client": "^0.15.2",
"async-listen": "^3.1.0",
"deep-diff": "^1.0.2",
diff --git a/packages/db/src/core/cli/commands/execute/index.ts b/packages/db/src/core/cli/commands/execute/index.ts
index 053736291..bf3dfd523 100644
--- a/packages/db/src/core/cli/commands/execute/index.ts
+++ b/packages/db/src/core/cli/commands/execute/index.ts
@@ -11,7 +11,7 @@ import {
} from '../../../errors.js';
import {
getLocalVirtualModContents,
- getStudioVirtualModContents,
+ getRemoteVirtualModContents,
} from '../../../integration/vite-plugin-db.js';
import { bundleFile, importBundledFile } from '../../../load-file.js';
import type { DBConfig } from '../../../types.js';
@@ -40,10 +40,9 @@ export async function cmd({
let virtualModContents: string;
if (flags.remote) {
- const appToken = await getManagedRemoteToken(flags.token);
- virtualModContents = getStudioVirtualModContents({
+ virtualModContents = getRemoteVirtualModContents({
tables: dbConfig.tables ?? {},
- appToken: appToken.token,
+ appToken: getManagedRemoteToken(flags.token),
isBuild: false,
output: 'server',
});
diff --git a/packages/db/src/core/cli/commands/link/index.ts b/packages/db/src/core/cli/commands/link/index.ts
deleted file mode 100644
index 4a105df9d..000000000
--- a/packages/db/src/core/cli/commands/link/index.ts
+++ /dev/null
@@ -1,295 +0,0 @@
-import { mkdir, writeFile } from 'node:fs/promises';
-import { homedir } from 'node:os';
-import { basename } from 'node:path';
-import {
- MISSING_SESSION_ID_ERROR,
- PROJECT_ID_FILE,
- getAstroStudioUrl,
- getSessionIdFromFile,
-} from '@astrojs/studio';
-import { slug } from 'github-slugger';
-import { bgRed, cyan } from 'kleur/colors';
-import prompts from 'prompts';
-import yoctoSpinner from 'yocto-spinner';
-import { safeFetch } from '../../../../runtime/utils.js';
-import type { Result } from '../../../utils.js';
-
-export async function cmd() {
- const sessionToken = await getSessionIdFromFile();
- if (!sessionToken) {
- console.error(MISSING_SESSION_ID_ERROR);
- process.exit(1);
- }
- await promptBegin();
- const isLinkExisting = await promptLinkExisting();
- if (isLinkExisting) {
- const workspaceId = await promptWorkspace(sessionToken);
- const existingProjectData = await promptExistingProjectName({ workspaceId });
- return await linkProject(existingProjectData.id);
- }
-
- const isLinkNew = await promptLinkNew();
- if (isLinkNew) {
- const workspaceId = await promptWorkspace(sessionToken);
- const newProjectName = await promptNewProjectName();
- const newProjectRegion = await promptNewProjectRegion();
- const spinner = yoctoSpinner({ text: 'Creating new project...' }).start();
- const newProjectData = await createNewProject({
- workspaceId,
- name: newProjectName,
- region: newProjectRegion,
- });
- // TODO(fks): Actually listen for project creation before continuing
- // This is just a dumb spinner that roughly matches database creation time.
- await new Promise((r) => setTimeout(r, 4000));
- spinner.success('Project created!');
- return await linkProject(newProjectData.id);
- }
-}
-
-async function linkProject(id: string) {
- await mkdir(new URL('.', PROJECT_ID_FILE), { recursive: true });
- await writeFile(PROJECT_ID_FILE, `${id}`);
- console.info('Project linked.');
-}
-
-async function getWorkspaces(sessionToken: string) {
- const linkUrl = new URL(getAstroStudioUrl() + '/api/cli/workspaces.list');
- const response = await safeFetch(
- linkUrl,
- {
- method: 'POST',
- headers: {
- Authorization: `Bearer ${sessionToken}`,
- 'Content-Type': 'application/json',
- },
- },
- (res) => {
- // Unauthorized
- if (res.status === 401) {
- throw new Error(
- `${bgRed('Unauthorized')}\n\n Are you logged in?\n Run ${cyan(
- 'astro login',
- )} to authenticate and then try linking again.\n\n`,
- );
- }
- throw new Error(`Failed to fetch user workspace: ${res.status} ${res.statusText}`);
- },
- );
-
- const { data, success } = (await response.json()) as Result<{ id: string; name: string }[]>;
- if (!success) {
- throw new Error(`Failed to fetch user's workspace.`);
- }
- return data;
-}
-
-/**
- * Get the workspace ID to link to.
- * Prompts the user to choose if they have more than one workspace in Astro Studio.
- * @returns A `Promise` for the workspace ID to use.
- */
-async function promptWorkspace(sessionToken: string) {
- const workspaces = await getWorkspaces(sessionToken);
- if (workspaces.length === 0) {
- console.error('No workspaces found.');
- process.exit(1);
- }
-
- if (workspaces.length === 1) {
- return workspaces[0].id;
- }
-
- const { workspaceId } = await prompts({
- type: 'autocomplete',
- name: 'workspaceId',
- message: 'Select your workspace:',
- limit: 5,
- choices: workspaces.map((w) => ({ title: w.name, value: w.id })),
- });
- if (typeof workspaceId !== 'string') {
- console.log('Canceled.');
- process.exit(0);
- }
- return workspaceId;
-}
-
-async function createNewProject({
- workspaceId,
- name,
- region,
-}: {
- workspaceId: string;
- name: string;
- region: string;
-}) {
- const linkUrl = new URL(getAstroStudioUrl() + '/api/cli/projects.create');
- const response = await safeFetch(
- linkUrl,
- {
- method: 'POST',
- headers: {
- Authorization: `Bearer ${await getSessionIdFromFile()}`,
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ workspaceId, name, region }),
- },
- (res) => {
- // Unauthorized
- if (res.status === 401) {
- console.error(
- `${bgRed('Unauthorized')}\n\n Are you logged in?\n Run ${cyan(
- 'astro login',
- )} to authenticate and then try linking again.\n\n`,
- );
- process.exit(1);
- }
- console.error(`Failed to create project: ${res.status} ${res.statusText}`);
- process.exit(1);
- },
- );
-
- const { data, success } = (await response.json()) as Result<{ id: string; idName: string }>;
- if (!success) {
- console.error(`Failed to create project.`);
- process.exit(1);
- }
- return { id: data.id, idName: data.idName };
-}
-
-async function promptExistingProjectName({ workspaceId }: { workspaceId: string }) {
- const linkUrl = new URL(getAstroStudioUrl() + '/api/cli/projects.list');
- const response = await safeFetch(
- linkUrl,
- {
- method: 'POST',
- headers: {
- Authorization: `Bearer ${await getSessionIdFromFile()}`,
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ workspaceId }),
- },
- (res) => {
- if (res.status === 401) {
- console.error(
- `${bgRed('Unauthorized')}\n\n Are you logged in?\n Run ${cyan(
- 'astro login',
- )} to authenticate and then try linking again.\n\n`,
- );
- process.exit(1);
- }
- console.error(`Failed to fetch projects: ${res.status} ${res.statusText}`);
- process.exit(1);
- },
- );
-
- const { data, success } = (await response.json()) as Result<
- { id: string; name: string; idName: string }[]
- >;
- if (!success) {
- console.error(`Failed to fetch projects.`);
- process.exit(1);
- }
- const { projectId } = await prompts({
- type: 'autocomplete',
- name: 'projectId',
- message: 'What is your project name?',
- limit: 5,
- choices: data.map((p) => ({ title: p.name, value: p.id })),
- });
- if (typeof projectId !== 'string') {
- console.log('Canceled.');
- process.exit(0);
- }
- const selectedProjectData = data.find((p: any) => p.id === projectId)!;
- return selectedProjectData;
-}
-
-async function promptBegin(): Promise<void> {
- // Get the current working directory relative to the user's home directory
- const prettyCwd = process.cwd().replace(homedir(), '~');
-
- // prompt
- const { begin } = await prompts({
- type: 'confirm',
- name: 'begin',
- message: `Link "${prettyCwd}" with Astro Studio?`,
- initial: true,
- });
- if (!begin) {
- console.log('Canceled.');
- process.exit(0);
- }
-}
-
-/**
- * Ask the user if they want to link to an existing Astro Studio project.
- * @returns A `Promise` for the user’s answer: `true` if they answer yes, otherwise `false`.
- */
-async function promptLinkExisting(): Promise<boolean> {
- // prompt
- const { linkExisting } = await prompts({
- type: 'confirm',
- name: 'linkExisting',
- message: `Link with an existing project in Astro Studio?`,
- initial: true,
- });
- return !!linkExisting;
-}
-
-/**
- * Ask the user if they want to link to a new Astro Studio Project.
- * **Exits the process if they answer no.**
- * @returns A `Promise` for the user’s answer: `true` if they answer yes.
- */
-async function promptLinkNew(): Promise<boolean> {
- // prompt
- const { linkNew } = await prompts({
- type: 'confirm',
- name: 'linkNew',
- message: `Create a new project in Astro Studio?`,
- initial: true,
- });
- if (!linkNew) {
- console.log('Canceled.');
- process.exit(0);
- }
- return true;
-}
-
-async function promptNewProjectName(): Promise<string> {
- const { newProjectName } = await prompts({
- type: 'text',
- name: 'newProjectName',
- message: `What is your new project's name?`,
- initial: basename(process.cwd()),
- format: (val) => slug(val),
- });
- if (!newProjectName) {
- console.log('Canceled.');
- process.exit(0);
- }
- return newProjectName;
-}
-
-async function promptNewProjectRegion(): Promise<string> {
- const { newProjectRegion } = await prompts({
- type: 'select',
- name: 'newProjectRegion',
- message: `Where should your new database live?`,
- choices: [
- { title: 'North America (East)', value: 'NorthAmericaEast' },
- { title: 'North America (West)', value: 'NorthAmericaWest' },
- { title: 'Europe (Amsterdam)', value: 'EuropeCentral' },
- { title: 'South America (Brazil)', value: 'SouthAmericaEast' },
- { title: 'Asia (India)', value: 'AsiaSouth' },
- { title: 'Asia (Japan)', value: 'AsiaNorthEast' },
- ],
- initial: 0,
- });
- if (!newProjectRegion) {
- console.log('Canceled.');
- process.exit(0);
- }
- return newProjectRegion;
-}
diff --git a/packages/db/src/core/cli/commands/login/index.ts b/packages/db/src/core/cli/commands/login/index.ts
deleted file mode 100644
index 0b0979384..000000000
--- a/packages/db/src/core/cli/commands/login/index.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-import { mkdir, writeFile } from 'node:fs/promises';
-import { createServer as _createServer } from 'node:http';
-import { SESSION_LOGIN_FILE, getAstroStudioUrl } from '@astrojs/studio';
-import type { AstroConfig } from 'astro';
-import { listen } from 'async-listen';
-import { cyan } from 'kleur/colors';
-import open from 'open';
-import prompt from 'prompts';
-import type { Arguments } from 'yargs-parser';
-import yoctoSpinner from 'yocto-spinner';
-import type { DBConfig } from '../../../types.js';
-
-const isWebContainer =
- // Stackblitz heuristic
- process.versions?.webcontainer ??
- // GitHub Codespaces heuristic
- process.env.CODESPACE_NAME;
-
-export async function cmd({
- flags,
-}: {
- astroConfig: AstroConfig;
- dbConfig: DBConfig;
- flags: Arguments;
-}) {
- let session = flags.session;
-
- if (!session && isWebContainer) {
- console.log(`Please visit the following URL in your web browser:`);
- console.log(cyan(`${getAstroStudioUrl()}/auth/cli/login`));
- console.log(`After login in complete, enter the verification code displayed:`);
- const response = await prompt({
- type: 'text',
- name: 'session',
- message: 'Verification code:',
- });
- if (!response.session) {
- console.error('Cancelling login.');
- process.exit(0);
- }
- session = response.session;
- console.log('Successfully logged in');
- } else if (!session) {
- const { url, promise } = await createServer();
- const loginUrl = new URL('/auth/cli/login', getAstroStudioUrl());
- loginUrl.searchParams.set('returnTo', url);
- console.log(`Opening the following URL in your browser...`);
- console.log(cyan(loginUrl.href));
- console.log(`If something goes wrong, copy-and-paste the URL into your browser.`);
- open(loginUrl.href);
- const spinner = yoctoSpinner({ text: 'Waiting for confirmation...' });
- session = await promise;
- spinner.success('Successfully logged in');
- }
-
- await mkdir(new URL('.', SESSION_LOGIN_FILE), { recursive: true });
- await writeFile(SESSION_LOGIN_FILE, `${session}`);
-}
-
-// NOTE(fks): How the Astro CLI login process works:
-// 1. The Astro CLI creates a temporary server to listen for the session token
-// 2. The user is directed to studio.astro.build/ to login
-// 3. The user is redirected back to the temporary server with their session token
-// 4. The temporary server receives and saves the session token, logging the user in
-// 5. The user is redirected one last time to a success/failure page
-async function createServer(): Promise<{ url: string; promise: Promise<string> }> {
- let resolve: (value: string | PromiseLike<string>) => void, reject: (reason?: Error) => void;
-
- const server = _createServer((req, res) => {
- // Handle the request
- const url = new URL(req.url ?? '/', `http://${req.headers.host}`);
- const sessionParam = url.searchParams.get('session');
- // Handle the response & resolve the promise
- res.statusCode = 302;
- if (!sessionParam) {
- res.setHeader('location', getAstroStudioUrl() + '/auth/cli/error');
- reject(new Error('Failed to log in'));
- } else {
- res.setHeader('location', getAstroStudioUrl() + '/auth/cli/success');
- resolve(sessionParam);
- }
- res.end();
- });
-
- const { port } = await listen(server, 0, '127.0.0.1');
- const serverUrl = `http://localhost:${port}`;
- const sessionPromise = new Promise<string>((_resolve, _reject) => {
- resolve = _resolve;
- reject = _reject;
- }).finally(() => {
- server.closeAllConnections();
- server.close();
- });
-
- return { url: serverUrl, promise: sessionPromise };
-}
diff --git a/packages/db/src/core/cli/commands/logout/index.ts b/packages/db/src/core/cli/commands/logout/index.ts
deleted file mode 100644
index 8b7878659..000000000
--- a/packages/db/src/core/cli/commands/logout/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { unlink } from 'node:fs/promises';
-import { SESSION_LOGIN_FILE } from '@astrojs/studio';
-
-export async function cmd() {
- await unlink(SESSION_LOGIN_FILE);
- console.log('Successfully logged out of Astro Studio.');
-}
diff --git a/packages/db/src/core/cli/commands/push/index.ts b/packages/db/src/core/cli/commands/push/index.ts
index 590d4f06e..46a457884 100644
--- a/packages/db/src/core/cli/commands/push/index.ts
+++ b/packages/db/src/core/cli/commands/push/index.ts
@@ -3,12 +3,10 @@ import { sql } from 'drizzle-orm';
import prompts from 'prompts';
import type { Arguments } from 'yargs-parser';
import { createRemoteDatabaseClient } from '../../../../runtime/index.js';
-import { safeFetch } from '../../../../runtime/utils.js';
import { MIGRATION_VERSION } from '../../../consts.js';
import type { DBConfig, DBSnapshot } from '../../../types.js';
import {
type RemoteDatabaseInfo,
- type Result,
getManagedRemoteToken,
getRemoteDatabaseInfo,
} from '../../../utils.js';
@@ -31,10 +29,10 @@ export async function cmd({
const isDryRun = flags.dryRun;
const isForceReset = flags.forceReset;
const dbInfo = getRemoteDatabaseInfo();
- const appToken = await getManagedRemoteToken(flags.token, dbInfo);
+ const appToken = getManagedRemoteToken(flags.token);
const productionSnapshot = await getProductionCurrentSnapshot({
dbInfo,
- appToken: appToken.token,
+ appToken: appToken,
});
const currentSnapshot = createCurrentSnapshot(dbConfig);
const isFromScratch = !productionSnapshot;
@@ -77,13 +75,11 @@ export async function cmd({
await pushSchema({
statements: migrationQueries,
dbInfo,
- appToken: appToken.token,
+ appToken: appToken,
isDryRun,
currentSnapshot: currentSnapshot,
});
}
- // cleanup and exit
- await appToken.destroy();
console.info('Push complete!');
}
@@ -110,9 +106,7 @@ async function pushSchema({
return new Response(null, { status: 200 });
}
- return dbInfo.type === 'studio'
- ? pushToStudio(requestBody, appToken, dbInfo.url)
- : pushToDb(requestBody, appToken, dbInfo.url);
+ return pushToDb(requestBody, appToken, dbInfo.url);
}
type RequestBody = {
@@ -145,29 +139,3 @@ async function pushToDb(requestBody: RequestBody, appToken: string, remoteUrl: s
)`);
});
}
-
-async function pushToStudio(requestBody: RequestBody, appToken: string, remoteUrl: string) {
- const url = new URL('/db/push', remoteUrl);
- const response = await safeFetch(
- url,
- {
- method: 'POST',
- headers: new Headers({
- Authorization: `Bearer ${appToken}`,
- }),
- body: JSON.stringify(requestBody),
- },
- async (res) => {
- console.error(`${url.toString()} failed: ${res.status} ${res.statusText}`);
- console.error(await res.text());
- throw new Error(`/db/push fetch failed: ${res.status} ${res.statusText}`);
- },
- );
-
- const result = (await response.json()) as Result<never>;
- if (!result.success) {
- console.error(`${url.toString()} unsuccessful`);
- console.error(await response.text());
- throw new Error(`/db/push fetch unsuccessful`);
- }
-}
diff --git a/packages/db/src/core/cli/commands/shell/index.ts b/packages/db/src/core/cli/commands/shell/index.ts
index dcc54fc70..278830b4e 100644
--- a/packages/db/src/core/cli/commands/shell/index.ts
+++ b/packages/db/src/core/cli/commands/shell/index.ts
@@ -26,14 +26,12 @@ export async function cmd({
}
const dbInfo = getRemoteDatabaseInfo();
if (flags.remote) {
- const appToken = await getManagedRemoteToken(flags.token, dbInfo);
const db = createRemoteDatabaseClient({
dbType: dbInfo.type,
remoteUrl: dbInfo.url,
- appToken: appToken.token,
+ appToken: getManagedRemoteToken(flags.token),
});
const result = await db.run(sql.raw(query));
- await appToken.destroy();
console.log(result);
} else {
const { ASTRO_DATABASE_FILE } = getAstroEnv();
diff --git a/packages/db/src/core/cli/commands/verify/index.ts b/packages/db/src/core/cli/commands/verify/index.ts
index 35f489a80..6499c272e 100644
--- a/packages/db/src/core/cli/commands/verify/index.ts
+++ b/packages/db/src/core/cli/commands/verify/index.ts
@@ -20,10 +20,9 @@ export async function cmd({
}) {
const isJson = flags.json;
const dbInfo = getRemoteDatabaseInfo();
- const appToken = await getManagedRemoteToken(flags.token, dbInfo);
const productionSnapshot = await getProductionCurrentSnapshot({
dbInfo,
- appToken: appToken.token,
+ appToken: getManagedRemoteToken(flags.token),
});
const currentSnapshot = createCurrentSnapshot(dbConfig);
const { queries: migrationQueries, confirmations } = await getMigrationQueries({
@@ -53,6 +52,5 @@ export async function cmd({
console.log(result.message);
}
- await appToken.destroy();
process.exit(result.exitCode);
}
diff --git a/packages/db/src/core/cli/index.ts b/packages/db/src/core/cli/index.ts
index 531b016a6..6c54c2ae6 100644
--- a/packages/db/src/core/cli/index.ts
+++ b/packages/db/src/core/cli/index.ts
@@ -41,18 +41,6 @@ export async function cli({
const { cmd } = await import('./commands/execute/index.js');
return await cmd({ astroConfig, dbConfig, flags });
}
- case 'login': {
- const { cmd } = await import('./commands/login/index.js');
- return await cmd({ astroConfig, dbConfig, flags });
- }
- case 'logout': {
- const { cmd } = await import('./commands/logout/index.js');
- return await cmd();
- }
- case 'link': {
- const { cmd } = await import('./commands/link/index.js');
- return await cmd();
- }
default: {
if (command != null) {
console.error(`Unknown command: ${command}`);
@@ -63,15 +51,15 @@ export async function cli({
headline: ' ',
tables: {
Commands: [
- ['push', 'Push table schema updates to Astro Studio.'],
- ['verify', 'Test schema updates /w Astro Studio (good for CI).'],
+ ['push', 'Push table schema updates to libSQL.'],
+ ['verify', 'Test schema updates /w libSQL (good for CI).'],
[
'astro db execute <file-path>',
- 'Execute a ts/js file using astro:db. Use --remote to connect to Studio.',
+ 'Execute a ts/js file using astro:db. Use --remote to connect to libSQL.',
],
[
'astro db shell --query <sql-string>',
- 'Execute a SQL string. Use --remote to connect to Studio.',
+ 'Execute a SQL string. Use --remote to connect to libSQL.',
],
],
},
diff --git a/packages/db/src/core/cli/migration-queries.ts b/packages/db/src/core/cli/migration-queries.ts
index db3972d09..0f369e684 100644
--- a/packages/db/src/core/cli/migration-queries.ts
+++ b/packages/db/src/core/cli/migration-queries.ts
@@ -7,7 +7,7 @@ import { customAlphabet } from 'nanoid';
import { hasPrimaryKey } from '../../runtime/index.js';
import { createRemoteDatabaseClient } from '../../runtime/index.js';
import { isSerializedSQL } from '../../runtime/types.js';
-import { isDbError, safeFetch } from '../../runtime/utils.js';
+import { isDbError } from '../../runtime/utils.js';
import { MIGRATION_VERSION } from '../consts.js';
import { RENAME_COLUMN_ERROR, RENAME_TABLE_ERROR } from '../errors.js';
import {
@@ -35,7 +35,7 @@ import type {
ResolvedIndexes,
TextColumn,
} from '../types.js';
-import type { RemoteDatabaseInfo, Result } from '../utils.js';
+import type { RemoteDatabaseInfo } from '../utils.js';
const sqlite = new SQLiteAsyncDialect();
const genTempTableName = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10);
@@ -428,9 +428,7 @@ export function getProductionCurrentSnapshot(options: {
dbInfo: RemoteDatabaseInfo;
appToken: string;
}): Promise<DBSnapshot | undefined> {
- return options.dbInfo.type === 'studio'
- ? getStudioCurrentSnapshot(options.appToken, options.dbInfo.url)
- : getDbCurrentSnapshot(options.appToken, options.dbInfo.url);
+ return getDbCurrentSnapshot(options.appToken, options.dbInfo.url);
}
async function getDbCurrentSnapshot(
@@ -473,36 +471,6 @@ async function getDbCurrentSnapshot(
}
}
-async function getStudioCurrentSnapshot(
- appToken: string,
- remoteUrl: string,
-): Promise<DBSnapshot | undefined> {
- const url = new URL('/db/schema', remoteUrl);
-
- const response = await safeFetch(
- url,
- {
- method: 'POST',
- headers: new Headers({
- Authorization: `Bearer ${appToken}`,
- }),
- },
- async (res) => {
- console.error(`${url.toString()} failed: ${res.status} ${res.statusText}`);
- console.error(await res.text());
- throw new Error(`/db/schema fetch failed: ${res.status} ${res.statusText}`);
- },
- );
-
- const result = (await response.json()) as Result<DBSnapshot>;
- if (!result.success) {
- console.error(`${url.toString()} unsuccessful`);
- console.error(await response.text());
- throw new Error(`/db/schema fetch unsuccessful`);
- }
- return result.data;
-}
-
function getDropTableQueriesForSnapshot(snapshot: DBSnapshot) {
const queries = [];
for (const tableName of Object.keys(snapshot.schema)) {
diff --git a/packages/db/src/core/integration/index.ts b/packages/db/src/core/integration/index.ts
index c6f58d2fd..bc038c7d2 100644
--- a/packages/db/src/core/integration/index.ts
+++ b/packages/db/src/core/integration/index.ts
@@ -2,7 +2,6 @@ import { existsSync } from 'node:fs';
import { mkdir, writeFile } from 'node:fs/promises';
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
-import type { ManagedAppToken } from '@astrojs/studio';
import type { AstroIntegration } from 'astro';
import { blue, yellow } from 'kleur/colors';
import {
@@ -33,7 +32,6 @@ function astroDBIntegration(): AstroIntegration {
let connectToRemote = false;
let configFileDependencies: string[] = [];
let root: URL;
- let appToken: ManagedAppToken | undefined;
// Used during production builds to load seed files.
let tempViteServer: ViteDevServer | undefined;
@@ -72,10 +70,9 @@ function astroDBIntegration(): AstroIntegration {
connectToRemote = process.env.ASTRO_INTERNAL_TEST_REMOTE || args['remote'];
if (connectToRemote) {
- appToken = await getManagedRemoteToken();
dbPlugin = vitePluginDb({
- connectToStudio: connectToRemote,
- appToken: appToken.token,
+ connectToRemote: connectToRemote,
+ appToken: getManagedRemoteToken(),
tables,
root: config.root,
srcDir: config.srcDir,
@@ -84,7 +81,7 @@ function astroDBIntegration(): AstroIntegration {
});
} else {
dbPlugin = vitePluginDb({
- connectToStudio: false,
+ connectToRemote: false,
tables,
seedFiles,
root: config.root,
@@ -161,7 +158,7 @@ function astroDBIntegration(): AstroIntegration {
if (!connectToRemote && !databaseFileEnvDefined() && finalBuildOutput === 'server') {
const message = `Attempting to build without the --remote flag or the ASTRO_DATABASE_FILE environment variable defined. You probably want to pass --remote to astro build.`;
const hint =
- 'Learn more connecting to Studio: https://docs.astro.build/en/guides/astro-db/#connect-to-astro-studio';
+ 'Learn more connecting to libSQL: https://docs.astro.build/en/guides/astro-db/#connect-a-libsql-database-for-production';
throw new AstroDbError(message, hint);
}
@@ -174,7 +171,6 @@ function astroDBIntegration(): AstroIntegration {
};
},
'astro:build:done': async ({}) => {
- await appToken?.destroy();
await tempViteServer?.close();
},
},
diff --git a/packages/db/src/core/integration/vite-plugin-db.ts b/packages/db/src/core/integration/vite-plugin-db.ts
index 410d49157..7a7173bfc 100644
--- a/packages/db/src/core/integration/vite-plugin-db.ts
+++ b/packages/db/src/core/integration/vite-plugin-db.ts
@@ -34,7 +34,7 @@ export type SeedHandler = {
type VitePluginDBParams =
| {
- connectToStudio: false;
+ connectToRemote: false;
tables: LateTables;
seedFiles: LateSeedFiles;
srcDir: URL;
@@ -44,7 +44,7 @@ type VitePluginDBParams =
seedHandler: SeedHandler;
}
| {
- connectToStudio: true;
+ connectToRemote: true;
tables: LateTables;
appToken: string;
srcDir: URL;
@@ -71,8 +71,8 @@ export function vitePluginDb(params: VitePluginDBParams): VitePlugin {
async load(id) {
if (id !== resolved.module && id !== resolved.importedFromSeedFile) return;
- if (params.connectToStudio) {
- return getStudioVirtualModContents({
+ if (params.connectToRemote) {
+ return getRemoteVirtualModContents({
appToken: params.appToken,
tables: params.tables.get(),
isBuild: command === 'build',
@@ -138,7 +138,7 @@ export * from ${RUNTIME_VIRTUAL_IMPORT};
${getStringifiedTableExports(tables)}`;
}
-export function getStudioVirtualModContents({
+export function getRemoteVirtualModContents({
tables,
appToken,
isBuild,
@@ -153,13 +153,12 @@ export function getStudioVirtualModContents({
function appTokenArg() {
if (isBuild) {
- const envPrefix = dbInfo.type === 'studio' ? 'ASTRO_STUDIO' : 'ASTRO_DB';
if (output === 'server') {
// In production build, always read the runtime environment variable.
- return `process.env.${envPrefix}_APP_TOKEN`;
+ return `process.env.ASTRO_DB_APP_TOKEN`;
} else {
// Static mode or prerendering needs the local app token.
- return `process.env.${envPrefix}_APP_TOKEN ?? ${JSON.stringify(appToken)}`;
+ return `process.env.ASTRO_DB_APP_TOKEN ?? ${JSON.stringify(appToken)}`;
}
} else {
return JSON.stringify(appToken);
@@ -171,9 +170,7 @@ export function getStudioVirtualModContents({
if (isBuild) {
// Allow overriding, mostly for testing
- return dbInfo.type === 'studio'
- ? `import.meta.env.ASTRO_STUDIO_REMOTE_DB_URL ?? ${dbStr}`
- : `import.meta.env.ASTRO_DB_REMOTE_URL ?? ${dbStr}`;
+ return `import.meta.env.ASTRO_DB_REMOTE_URL ?? ${dbStr}`;
} else {
return dbStr;
}
diff --git a/packages/db/src/core/load-file.ts b/packages/db/src/core/load-file.ts
index 027deaa60..f7a4226b6 100644
--- a/packages/db/src/core/load-file.ts
+++ b/packages/db/src/core/load-file.ts
@@ -144,8 +144,8 @@ export async function bundleFile({
format: 'esm',
sourcemap: 'inline',
metafile: true,
+ // TODO: use astro:env
define: {
- 'import.meta.env.ASTRO_STUDIO_REMOTE_DB_URL': 'undefined',
'import.meta.env.ASTRO_DB_REMOTE_DB_URL': 'undefined',
'import.meta.env.ASTRO_DATABASE_FILE': JSON.stringify(ASTRO_DATABASE_FILE ?? ''),
},
diff --git a/packages/db/src/core/schemas.ts b/packages/db/src/core/schemas.ts
index c9575a79a..1147538ce 100644
--- a/packages/db/src/core/schemas.ts
+++ b/packages/db/src/core/schemas.ts
@@ -94,7 +94,7 @@ const textColumnBaseSchema = baseColumnSchema
z.object({
// text primary key allows NULL values.
// NULL values bypass unique checks, which could
- // lead to duplicate URLs per record in Astro Studio.
+ // lead to duplicate URLs per record.
// disable `optional` for primary keys.
primaryKey: z.literal(true),
optional: z.literal(false).optional(),
diff --git a/packages/db/src/core/utils.ts b/packages/db/src/core/utils.ts
index b246997e2..3348e90a0 100644
--- a/packages/db/src/core/utils.ts
+++ b/packages/db/src/core/utils.ts
@@ -1,4 +1,3 @@
-import { type ManagedAppToken, getAstroStudioEnv, getManagedAppTokenOrExit } from '@astrojs/studio';
import type { AstroConfig, AstroIntegration } from 'astro';
import { loadEnv } from 'vite';
import './types.js';
@@ -11,49 +10,23 @@ export function getAstroEnv(envMode = ''): Record<`ASTRO_${string}`, string> {
}
export type RemoteDatabaseInfo = {
- type: 'libsql' | 'studio';
+ type: 'libsql';
url: string;
};
export function getRemoteDatabaseInfo(): RemoteDatabaseInfo {
const astroEnv = getAstroEnv();
- const studioEnv = getAstroStudioEnv();
-
- if (studioEnv.ASTRO_STUDIO_REMOTE_DB_URL)
- return {
- type: 'studio',
- url: studioEnv.ASTRO_STUDIO_REMOTE_DB_URL,
- };
-
- if (astroEnv.ASTRO_DB_REMOTE_URL)
- return {
- type: 'libsql',
- url: astroEnv.ASTRO_DB_REMOTE_URL,
- };
return {
- type: 'studio',
- url: 'https://db.services.astro.build',
+ type: 'libsql',
+ url: astroEnv.ASTRO_DB_REMOTE_URL,
};
}
-export function getManagedRemoteToken(
- token?: string,
- dbInfo?: RemoteDatabaseInfo,
-): Promise<ManagedAppToken> {
- dbInfo ??= getRemoteDatabaseInfo();
-
- if (dbInfo.type === 'studio') {
- return getManagedAppTokenOrExit(token);
- }
-
+export function getManagedRemoteToken(token?: string): string {
const astroEnv = getAstroEnv();
- return Promise.resolve({
- token: token ?? astroEnv.ASTRO_DB_APP_TOKEN,
- renew: () => Promise.resolve(),
- destroy: () => Promise.resolve(),
- });
+ return token ?? astroEnv.ASTRO_DB_APP_TOKEN;
}
export function getDbDirectoryUrl(root: URL | string) {
diff --git a/packages/db/src/runtime/db-client.ts b/packages/db/src/runtime/db-client.ts
index 55288951d..e45a2d717 100644
--- a/packages/db/src/runtime/db-client.ts
+++ b/packages/db/src/runtime/db-client.ts
@@ -1,10 +1,7 @@
-import type { InStatement } from '@libsql/client';
import { type Config as LibSQLConfig, createClient } from '@libsql/client';
import type { LibSQLDatabase } from 'drizzle-orm/libsql';
import { drizzle as drizzleLibsql } from 'drizzle-orm/libsql';
-import { type SqliteRemoteDatabase, drizzle as drizzleProxy } from 'drizzle-orm/sqlite-proxy';
-import { z } from 'zod';
-import { DetailedLibsqlError, safeFetch } from './utils.js';
+import type { SqliteRemoteDatabase } from 'drizzle-orm/sqlite-proxy';
const isWebContainer = !!process.versions?.webcontainer;
@@ -34,16 +31,8 @@ export function createLocalDatabaseClient(options: LocalDbClientOptions): LibSQL
return db;
}
-const remoteResultSchema = z.object({
- columns: z.array(z.string()),
- columnTypes: z.array(z.string()),
- rows: z.array(z.array(z.unknown())),
- rowsAffected: z.number(),
- lastInsertRowid: z.unknown().optional(),
-});
-
type RemoteDbClientOptions = {
- dbType: 'studio' | 'libsql';
+ dbType: 'libsql';
appToken: string;
remoteUrl: string | URL;
};
@@ -51,9 +40,7 @@ type RemoteDbClientOptions = {
export function createRemoteDatabaseClient(options: RemoteDbClientOptions) {
const remoteUrl = new URL(options.remoteUrl);
- return options.dbType === 'studio'
- ? createStudioDatabaseClient(options.appToken, remoteUrl)
- : createRemoteLibSQLClient(options.appToken, remoteUrl, options.remoteUrl.toString());
+ return createRemoteLibSQLClient(options.appToken, remoteUrl, options.remoteUrl.toString());
}
// this function parses the options from a `Record<string, string>`
@@ -99,167 +86,3 @@ function createRemoteLibSQLClient(appToken: string, remoteDbURL: URL, rawUrl: st
const client = createClient({ ...parseOpts(options), url, authToken: appToken });
return drizzleLibsql(client);
}
-
-function createStudioDatabaseClient(appToken: string, remoteDbURL: URL) {
- if (appToken == null) {
- throw new Error(`Cannot create a remote client: missing app token.`);
- }
-
- const url = new URL('/db/query', remoteDbURL);
-
- const db = drizzleProxy(
- async (sql, parameters, method) => {
- const requestBody: InStatement = { sql, args: parameters };
- const res = await safeFetch(
- url,
- {
- method: 'POST',
- headers: {
- Authorization: `Bearer ${appToken}`,
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(requestBody),
- },
- async (response) => {
- throw await parseRemoteError(response);
- },
- );
-
- let remoteResult: z.infer<typeof remoteResultSchema>;
- try {
- const json = await res.json();
- remoteResult = remoteResultSchema.parse(json);
- } catch {
- throw new DetailedLibsqlError({
- message: await getUnexpectedResponseMessage(res),
- code: KNOWN_ERROR_CODES.SQL_QUERY_FAILED,
- });
- }
-
- if (method === 'run') {
- const rawRows = Array.from(remoteResult.rows);
- // Implement basic `toJSON()` for Drizzle to serialize properly
- (remoteResult as any).rows.toJSON = () => rawRows;
- // Using `db.run()` drizzle massages the rows into an object.
- // So in order to make dev/prod consistent, we need to do the same here.
- // This creates an object and loops over each row replacing it with the object.
- for (let i = 0; i < remoteResult.rows.length; i++) {
- let row = remoteResult.rows[i];
- let item: Record<string, any> = {};
- remoteResult.columns.forEach((col, index) => {
- item[col] = row[index];
- });
- (remoteResult as any).rows[i] = item;
- }
- return remoteResult;
- }
-
- // Drizzle expects each row as an array of its values
- const rowValues: unknown[][] = [];
-
- for (const row of remoteResult.rows) {
- if (row != null && typeof row === 'object') {
- rowValues.push(Object.values(row));
- }
- }
-
- if (method === 'get') {
- return { rows: rowValues[0] };
- }
-
- return { rows: rowValues };
- },
- async (queries) => {
- const stmts: InStatement[] = queries.map(({ sql, params }) => ({ sql, args: params }));
- const res = await safeFetch(
- url,
- {
- method: 'POST',
- headers: {
- Authorization: `Bearer ${appToken}`,
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(stmts),
- },
- async (response) => {
- throw await parseRemoteError(response);
- },
- );
-
- let remoteResults: z.infer<typeof remoteResultSchema>[];
- try {
- const json = await res.json();
- remoteResults = z.array(remoteResultSchema).parse(json);
- } catch {
- throw new DetailedLibsqlError({
- message: await getUnexpectedResponseMessage(res),
- code: KNOWN_ERROR_CODES.SQL_QUERY_FAILED,
- });
- }
- let results: any[] = [];
- for (const [idx, rawResult] of remoteResults.entries()) {
- if (queries[idx]?.method === 'run') {
- results.push(rawResult);
- continue;
- }
-
- // Drizzle expects each row as an array of its values
- const rowValues: unknown[][] = [];
-
- for (const row of rawResult.rows) {
- if (row != null && typeof row === 'object') {
- rowValues.push(Object.values(row));
- }
- }
-
- if (queries[idx]?.method === 'get') {
- results.push({ rows: rowValues[0] });
- }
-
- results.push({ rows: rowValues });
- }
- return results;
- },
- );
- applyTransactionNotSupported(db);
- return db;
-}
-
-const errorSchema = z.object({
- success: z.boolean(),
- error: z.object({
- code: z.string(),
- details: z.string().optional(),
- }),
-});
-
-const KNOWN_ERROR_CODES = {
- SQL_QUERY_FAILED: 'SQL_QUERY_FAILED',
-};
-
-const getUnexpectedResponseMessage = async (response: Response) =>
- `Unexpected response from remote database:\n(Status ${response.status}) ${await response
- .clone()
- .text()}`;
-
-async function parseRemoteError(response: Response): Promise<DetailedLibsqlError> {
- let error;
- try {
- error = errorSchema.parse(await response.clone().json()).error;
- } catch {
- return new DetailedLibsqlError({
- message: await getUnexpectedResponseMessage(response),
- code: KNOWN_ERROR_CODES.SQL_QUERY_FAILED,
- });
- }
- // Strip LibSQL error prefixes
- let baseDetails =
- error.details?.replace(/.*SQLite error: /, '') ?? 'Error querying remote database.';
- // Remove duplicated "code" in details
- const details = baseDetails.slice(baseDetails.indexOf(':') + 1).trim();
- let hint = `See the Astro DB guide for query and push instructions: https://docs.astro.build/en/guides/astro-db/#query-your-database`;
- if (error.code === KNOWN_ERROR_CODES.SQL_QUERY_FAILED && details.includes('no such table')) {
- hint = `Did you run \`astro db push\` to push your latest table schemas?`;
- }
- return new DetailedLibsqlError({ message: details, code: error.code, hint });
-}
diff --git a/packages/db/test/basics.test.js b/packages/db/test/basics.test.js
index 8d6167447..02f5bce37 100644
--- a/packages/db/test/basics.test.js
+++ b/packages/db/test/basics.test.js
@@ -181,13 +181,13 @@ describe('astro:db', () => {
before(async () => {
clearEnvironment();
- process.env.ASTRO_STUDIO_APP_TOKEN = 'some token';
+ process.env.ASTRO_DB_APP_TOKEN = 'some token';
remoteDbServer = await setupRemoteDbServer(fixture.config);
await fixture.build();
});
after(async () => {
- process.env.ASTRO_STUDIO_APP_TOKEN = '';
+ process.env.ASTRO_DB_APP_TOKEN = '';
await remoteDbServer?.stop();
});
diff --git a/packages/db/test/ssr-no-apptoken.test.js b/packages/db/test/ssr-no-apptoken.test.js
index c570306e5..a78d7bf58 100644
--- a/packages/db/test/ssr-no-apptoken.test.js
+++ b/packages/db/test/ssr-no-apptoken.test.js
@@ -17,7 +17,7 @@ describe('missing app token', () => {
remoteDbServer = await setupRemoteDbServer(fixture.config);
await fixture.build();
// Ensure there's no token at runtime
- delete process.env.ASTRO_STUDIO_APP_TOKEN;
+ delete process.env.ASTRO_DB_APP_TOKEN;
});
after(async () => {
diff --git a/packages/db/test/test-utils.js b/packages/db/test/test-utils.js
index b608d75b8..b2a351a47 100644
--- a/packages/db/test/test-utils.js
+++ b/packages/db/test/test-utils.js
@@ -21,7 +21,7 @@ let portIncrementer = 8030;
*/
export async function setupRemoteDbServer(astroConfig) {
const port = portIncrementer++;
- process.env.ASTRO_STUDIO_REMOTE_DB_URL = `http://localhost:${port}`;
+ process.env.ASTRO_DB_REMOTE_DB_URL = `http://localhost:${port}`;
process.env.ASTRO_INTERNAL_TEST_REMOTE = true;
const server = createRemoteDbServer().listen(port);
@@ -50,7 +50,7 @@ export async function setupRemoteDbServer(astroConfig) {
return {
server,
async stop() {
- delete process.env.ASTRO_STUDIO_REMOTE_DB_URL;
+ delete process.env.ASTRO_DB_REMOTE_DB_URL;
delete process.env.ASTRO_INTERNAL_TEST_REMOTE;
return new Promise((resolve, reject) => {
server.close((err) => {
@@ -83,12 +83,12 @@ export async function initializeRemoteDb(astroConfig) {
}
/**
- * Clears the environment variables related to Astro DB and Astro Studio.
+ * Clears the environment variables related to Astro DB.
*/
export function clearEnvironment() {
const keys = Array.from(Object.keys(process.env));
for (const key of keys) {
- if (key.startsWith('ASTRO_DB_') || key.startsWith('ASTRO_STUDIO_')) {
+ if (key.startsWith('ASTRO_DB_')) {
delete process.env[key];
}
}
diff --git a/packages/db/test/unit/remote-info.test.js b/packages/db/test/unit/remote-info.test.js
index 2c58f28b7..c72d59b4f 100644
--- a/packages/db/test/unit/remote-info.test.js
+++ b/packages/db/test/unit/remote-info.test.js
@@ -8,6 +8,7 @@ describe('RemoteDatabaseInfo', () => {
clearEnvironment();
});
+ // TODO: what should be the default url for libsql?
test('default remote info', () => {
const dbInfo = getRemoteDatabaseInfo();
@@ -17,16 +18,6 @@ describe('RemoteDatabaseInfo', () => {
});
});
- test('configured Astro Studio remote', () => {
- process.env.ASTRO_STUDIO_REMOTE_DB_URL = 'https://studio.astro.build';
- const dbInfo = getRemoteDatabaseInfo();
-
- assert.deepEqual(dbInfo, {
- type: 'studio',
- url: 'https://studio.astro.build',
- });
- });
-
test('configured libSQL remote', () => {
process.env.ASTRO_DB_REMOTE_URL = 'libsql://libsql.self.hosted';
const dbInfo = getRemoteDatabaseInfo();
@@ -36,24 +27,12 @@ describe('RemoteDatabaseInfo', () => {
url: 'libsql://libsql.self.hosted',
});
});
-
- test('configured both libSQL and Studio remote', () => {
- process.env.ASTRO_DB_REMOTE_URL = 'libsql://libsql.self.hosted';
- process.env.ASTRO_STUDIO_REMOTE_DB_URL = 'https://studio.astro.build';
- const dbInfo = getRemoteDatabaseInfo();
-
- assert.deepEqual(dbInfo, {
- type: 'studio',
- url: 'https://studio.astro.build',
- });
- });
});
describe('RemoteManagedToken', () => {
// Avoid conflicts with other tests
beforeEach(() => {
clearEnvironment();
- process.env.ASTRO_STUDIO_APP_TOKEN = 'studio token';
process.env.ASTRO_DB_APP_TOKEN = 'db token';
});
after(() => {
@@ -68,20 +47,7 @@ describe('RemoteManagedToken', () => {
test('token for default remote', async () => {
const { token } = await getManagedRemoteToken();
- assert.equal(token, 'studio token');
- });
-
- test('given token for configured Astro Studio remote', async () => {
- process.env.ASTRO_STUDIO_REMOTE_DB_URL = 'https://studio.astro.build';
- const { token } = await getManagedRemoteToken('given token');
- assert.equal(token, 'given token');
- });
-
- test('token for configured Astro Studio remote', async () => {
- process.env.ASTRO_STUDIO_REMOTE_DB_URL = 'https://studio.astro.build';
- const { token } = await getManagedRemoteToken();
-
- assert.equal(token, 'studio token');
+ assert.equal(token, 'db token');
});
test('given token for configured libSQL remote', async () => {
@@ -97,18 +63,8 @@ describe('RemoteManagedToken', () => {
assert.equal(token, 'db token');
});
- test('token for given Astro Studio remote', async () => {
- process.env.ASTRO_DB_REMOTE_URL = 'libsql://libsql.self.hosted';
- const { token } = await getManagedRemoteToken(undefined, {
- type: 'studio',
- url: 'https://studio.astro.build',
- });
-
- assert.equal(token, 'studio token');
- });
-
test('token for given libSQL remote', async () => {
- process.env.ASTRO_STUDIO_REMOTE_URL = 'libsql://libsql.self.hosted';
+ process.env.ASTRO_DB_REMOTE_URL = 'libsql://libsql.self.hosted';
const { token } = await getManagedRemoteToken(undefined, {
type: 'libsql',
url: 'libsql://libsql.self.hosted',
diff --git a/packages/integrations/cloudflare/src/entrypoints/server.ts b/packages/integrations/cloudflare/src/entrypoints/server.ts
index a37f820ab..a236d4336 100644
--- a/packages/integrations/cloudflare/src/entrypoints/server.ts
+++ b/packages/integrations/cloudflare/src/entrypoints/server.ts
@@ -14,7 +14,6 @@ setGetEnv(createGetEnv(globalEnv as Env));
type Env = {
[key: string]: unknown;
ASSETS: { fetch: (req: Request | string) => Promise<Response> };
- ASTRO_STUDIO_APP_TOKEN?: string;
};
export interface Runtime<T extends object = object> {
@@ -73,12 +72,6 @@ export function createExports(manifest: SSRManifest) {
request.headers.get('cf-connecting-ip'),
);
- process.env.ASTRO_STUDIO_APP_TOKEN ??= (() => {
- if (typeof env.ASTRO_STUDIO_APP_TOKEN === 'string') {
- return env.ASTRO_STUDIO_APP_TOKEN;
- }
- })();
-
const locals: Runtime = {
runtime: {
env: env,
diff --git a/packages/studio/CHANGELOG.md b/packages/studio/CHANGELOG.md
deleted file mode 100644
index d9876af2b..000000000
--- a/packages/studio/CHANGELOG.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# @astrojs/studio
-
-## 0.1.9
-
-### Patch Changes
-
-- [#13731](https://github.com/withastro/astro/pull/13731) [`c3e80c2`](https://github.com/withastro/astro/commit/c3e80c25b90c803e2798b752583a8e77cdad3146) Thanks [@jsparkdev](https://github.com/jsparkdev)! - update vite to latest version for fixing CVE
-
-## 0.1.8
-
-### Patch Changes
-
-- [#13591](https://github.com/withastro/astro/pull/13591) [`5dd2d3f`](https://github.com/withastro/astro/commit/5dd2d3fde8a138ed611dedf39ffa5dfeeed315f8) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Removes unused code
-
-## 0.1.7
-
-### Patch Changes
-
-- [#13596](https://github.com/withastro/astro/pull/13596) [`3752519`](https://github.com/withastro/astro/commit/375251966d1b28a570bff45ff0fe7e7d2fe46f72) Thanks [@jsparkdev](https://github.com/jsparkdev)! - update vite to latest version to fix CVE
-
-- [#13547](https://github.com/withastro/astro/pull/13547) [`360cb91`](https://github.com/withastro/astro/commit/360cb9199a4314f90825c5639ff4396760e9cfcc) Thanks [@jsparkdev](https://github.com/jsparkdev)! - Updates vite to the latest version
-
-## 0.1.6
-
-### Patch Changes
-
-- [#13526](https://github.com/withastro/astro/pull/13526) [`ff9d69e`](https://github.com/withastro/astro/commit/ff9d69e3443c80059c54f6296d19f66bb068ead3) Thanks [@jsparkdev](https://github.com/jsparkdev)! - update `vite` to the latest version
-
-## 0.1.5
-
-### Patch Changes
-
-- [#13505](https://github.com/withastro/astro/pull/13505) [`a98ae5b`](https://github.com/withastro/astro/commit/a98ae5b8f5c33900379012e9e253a755c0a8927e) Thanks [@ematipico](https://github.com/ematipico)! - Updates the dependency `vite` to the latest.
-
-## 0.1.4
-
-### Patch Changes
-
-- [#13011](https://github.com/withastro/astro/pull/13011) [`cf30880`](https://github.com/withastro/astro/commit/cf3088060d45227dcb48e041c4ed5e0081d71398) Thanks [@ascorbic](https://github.com/ascorbic)! - Upgrades Vite
-
-## 0.1.3
-
-### Patch Changes
-
-- [#12799](https://github.com/withastro/astro/pull/12799) [`739dbfb`](https://github.com/withastro/astro/commit/739dbfba4214107cf8fc40c702834dad33eed3b0) Thanks [@ascorbic](https://github.com/ascorbic)! - Upgrades Vite to pin esbuild
-
-## 0.1.2
-
-### Patch Changes
-
-- [#12073](https://github.com/withastro/astro/pull/12073) [`acf264d`](https://github.com/withastro/astro/commit/acf264d8c003718cda5a0b9ce5fb7ac1cd6641b6) Thanks [@bluwy](https://github.com/bluwy)! - Replaces `ora` with `yocto-spinner`
-
-## 0.1.2-beta.0
-
-### Patch Changes
-
-- [#12073](https://github.com/withastro/astro/pull/12073) [`acf264d`](https://github.com/withastro/astro/commit/acf264d8c003718cda5a0b9ce5fb7ac1cd6641b6) Thanks [@bluwy](https://github.com/bluwy)! - Replaces `ora` with `yocto-spinner`
-
-## 0.1.1
-
-### Patch Changes
-
-- [#11331](https://github.com/withastro/astro/pull/11331) [`f1b78a4`](https://github.com/withastro/astro/commit/f1b78a496034d53b0e9dfc276a4a1b1d691772c4) Thanks [@bluwy](https://github.com/bluwy)! - Relaxes exports condition to allow importing ESM from CJS
-
-## 0.1.0
-
-### Minor Changes
-
-- [#11037](https://github.com/withastro/astro/pull/11037) [`9332bb1`](https://github.com/withastro/astro/commit/9332bb1c1f237f5666ded09532ccd651837b94e5) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Created package. This package contain shared code between integrations that interact with Astro Studio and is not intended to be used by end-users at this time
diff --git a/packages/studio/README.md b/packages/studio/README.md
deleted file mode 100644
index 935c200a3..000000000
--- a/packages/studio/README.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# @astrojs/studio
-
-This package manages the connection between a local Astro project and [Astro Studio](studio). At this time, this package is not intended for direct use by end users, but rather as a dependency of other Astro packages.
-
-## Support
-
-- Get help in the [Astro Discord][discord]. Post questions in our `#support` forum, or visit our dedicated `#dev` channel to discuss current development and more!
-
-- Check our [Astro Integration Documentation][astro-integration] for more on integrations.
-
-- Submit bug reports and feature requests as [GitHub issues][issues].
-
-## Contributing
-
-This package is maintained by Astro's Core team. You're welcome to submit an issue or PR! These links will help you get started:
-
-- [Contributor Manual][contributing]
-- [Code of Conduct][coc]
-- [Community Guide][community]
-
-## License
-
-MIT
-
-Copyright (c) 2023–present [Astro][astro]
-
-[astro]: https://astro.build/
-[contributing]: https://github.com/withastro/astro/blob/main/CONTRIBUTING.md
-[coc]: https://github.com/withastro/.github/blob/main/CODE_OF_CONDUCT.md
-[community]: https://github.com/withastro/.github/blob/main/COMMUNITY_GUIDE.md
-[discord]: https://astro.build/chat/
-[issues]: https://github.com/withastro/astro/issues
-[astro-integration]: https://docs.astro.build/en/guides/integrations-guide/
-[studio]: https://studio.astro.build/
diff --git a/packages/studio/package.json b/packages/studio/package.json
deleted file mode 100644
index 646bb77b1..000000000
--- a/packages/studio/package.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "name": "@astrojs/studio",
- "version": "0.1.9",
- "description": "Internal package powering integrations between Astro projects and Astro Studio",
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/withastro/astro.git",
- "directory": "packages/studio"
- },
- "bugs": "https://github.com/withastro/astro/issues",
- "homepage": "https://docs.astro.build/en/guides/integrations-guide/studio/",
- "type": "module",
- "author": "withastro",
- "types": "./dist/index.js",
- "main": "./dist/index.js",
- "exports": {
- ".": {
- "types": "./dist/index.d.ts",
- "default": "./dist/index.js"
- },
- "./package.json": "./package.json"
- },
- "files": [
- "dist"
- ],
- "keywords": [
- "withastro",
- "astro-integration"
- ],
- "scripts": {
- "build": "astro-scripts build \"src/**/*.ts\" && tsc",
- "build:ci": "astro-scripts build \"src/**/*.ts\"",
- "dev": "astro-scripts dev \"src/**/*.ts\""
- },
- "dependencies": {
- "ci-info": "^4.2.0",
- "kleur": "^4.1.5",
- "yocto-spinner": "^0.2.1"
- },
- "devDependencies": {
- "astro-scripts": "workspace:*",
- "typescript": "^5.8.3",
- "vite": "^6.3.4"
- }
-}
diff --git a/packages/studio/src/core/errors.ts b/packages/studio/src/core/errors.ts
deleted file mode 100644
index 9ffead3ee..000000000
--- a/packages/studio/src/core/errors.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { cyan, red } from 'kleur/colors';
-
-export const MISSING_SESSION_ID_CI_ERROR = `${red('▶ ASTRO_STUDIO_APP_TOKEN required')}
-
- To authenticate with Astro Studio add the token to your CI's environment variables.\n`;
-
-export const MISSING_SESSION_ID_ERROR = `${red('▶ Login required!')}
-
- To authenticate with Astro Studio, run
- ${cyan('astro login')}\n`;
-
-export const MISSING_PROJECT_ID_ERROR = `${red('▶ Directory not linked.')}
-
- To link this directory to an Astro Studio project, run
- ${cyan('astro link')}\n`;
diff --git a/packages/studio/src/core/tokens.ts b/packages/studio/src/core/tokens.ts
deleted file mode 100644
index cb43d70ff..000000000
--- a/packages/studio/src/core/tokens.ts
+++ /dev/null
@@ -1,225 +0,0 @@
-import { readFile } from 'node:fs/promises';
-import { homedir } from 'node:os';
-import { join } from 'node:path';
-import { pathToFileURL } from 'node:url';
-import ci from 'ci-info';
-import { green } from 'kleur/colors';
-import yoctoSpinner from 'yocto-spinner';
-import {
- MISSING_PROJECT_ID_ERROR,
- MISSING_SESSION_ID_CI_ERROR,
- MISSING_SESSION_ID_ERROR,
-} from './errors.js';
-import { getAstroStudioEnv, getAstroStudioUrl } from './utils.js';
-
-export const SESSION_LOGIN_FILE = pathToFileURL(join(homedir(), '.astro', 'session-token'));
-export const PROJECT_ID_FILE = pathToFileURL(join(process.cwd(), '.astro', 'link'));
-
-export interface ManagedAppToken {
- token: string;
- renew(): Promise<void>;
- destroy(): Promise<void>;
-}
-
-class ManagedLocalAppToken implements ManagedAppToken {
- token: string;
- constructor(token: string) {
- this.token = token;
- }
- async renew() {}
- async destroy() {}
-}
-
-class ManagedRemoteAppToken implements ManagedAppToken {
- token: string;
- session: string;
- projectId: string;
- ttl: number;
- expires: Date;
- renewTimer: NodeJS.Timeout | undefined;
-
- static async create(sessionToken: string, projectId: string) {
- const { token: shortLivedAppToken, ttl } = await this.createToken(sessionToken, projectId);
- return new ManagedRemoteAppToken({
- token: shortLivedAppToken,
- session: sessionToken,
- projectId,
- ttl,
- });
- }
-
- static async createToken(
- sessionToken: string,
- projectId: string,
- ): Promise<{ token: string; ttl: number }> {
- const spinner = yoctoSpinner({ text: 'Connecting to remote database...' }).start();
- const response = await safeFetch(
- new URL(`${getAstroStudioUrl()}/auth/cli/token-create`),
- {
- method: 'POST',
- headers: new Headers({
- Authorization: `Bearer ${sessionToken}`,
- }),
- body: JSON.stringify({ projectId }),
- },
- (res) => {
- throw new Error(`Failed to create token: ${res.status} ${res.statusText}`);
- },
- );
- spinner.success(green('Connected to remote database.'));
-
- const { token, ttl } = await response.json();
- return { token, ttl };
- }
-
- constructor(options: { token: string; session: string; projectId: string; ttl: number }) {
- this.token = options.token;
- this.session = options.session;
- this.projectId = options.projectId;
- this.ttl = options.ttl;
- this.renewTimer = setTimeout(() => this.renew(), (1000 * 60 * 5) / 2);
- this.expires = getExpiresFromTtl(this.ttl);
- }
-
- private async fetch(url: string, body: Record<string, unknown>) {
- return safeFetch(
- `${getAstroStudioUrl()}${url}`,
- {
- method: 'POST',
- headers: {
- Authorization: `Bearer ${this.session}`,
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(body),
- },
- () => {
- throw new Error(`Failed to fetch ${url}.`);
- },
- );
- }
-
- tokenIsValid() {
- return new Date() > this.expires;
- }
-
- createRenewTimer() {
- return setTimeout(() => this.renew(), (1000 * 60 * this.ttl) / 2);
- }
-
- async renew() {
- clearTimeout(this.renewTimer);
- delete this.renewTimer;
-
- if (this.tokenIsValid()) {
- const response = await this.fetch('/auth/cli/token-renew', {
- token: this.token,
- projectId: this.projectId,
- });
- if (response.status === 200) {
- this.expires = getExpiresFromTtl(this.ttl);
- this.renewTimer = this.createRenewTimer();
- } else {
- throw new Error(`Unexpected response: ${response.status} ${response.statusText}`);
- }
- } else {
- try {
- const { token, ttl } = await ManagedRemoteAppToken.createToken(
- this.session,
- this.projectId,
- );
- this.token = token;
- this.ttl = ttl;
- this.expires = getExpiresFromTtl(ttl);
- this.renewTimer = this.createRenewTimer();
- } catch {
- // If we get here we couldn't create a new token. Since the existing token
- // is expired we really can't do anything and should exit.
- throw new Error(
- `Token has expired and attempts to renew it have failed, please try again.`,
- );
- }
- }
- }
-
- async destroy() {
- try {
- const response = await this.fetch('/auth/cli/token-delete', {
- token: this.token,
- projectId: this.projectId,
- });
- if (response.status !== 200) {
- throw new Error(`Unexpected response: ${response.status} ${response.statusText}`);
- }
- } catch (error: any) {
- console.error('Failed to delete token.', error?.message);
- }
- }
-}
-
-export async function getProjectIdFromFile() {
- try {
- return await readFile(PROJECT_ID_FILE, 'utf-8');
- } catch {
- return undefined;
- }
-}
-
-export async function getSessionIdFromFile() {
- try {
- return await readFile(SESSION_LOGIN_FILE, 'utf-8');
- } catch {
- return undefined;
- }
-}
-
-export async function getManagedAppTokenOrExit(token?: string): Promise<ManagedAppToken> {
- if (token) {
- return new ManagedLocalAppToken(token);
- }
- if (process.env.ASTRO_INTERNAL_TEST_REMOTE) {
- return new ManagedLocalAppToken('fake' /* token ignored in test */);
- }
- const { ASTRO_STUDIO_APP_TOKEN } = getAstroStudioEnv();
- if (ASTRO_STUDIO_APP_TOKEN) {
- return new ManagedLocalAppToken(ASTRO_STUDIO_APP_TOKEN);
- }
- const sessionToken = await getSessionIdFromFile();
- if (!sessionToken) {
- if (ci.isCI) {
- console.error(MISSING_SESSION_ID_CI_ERROR);
- } else {
- console.error(MISSING_SESSION_ID_ERROR);
- }
- process.exit(1);
- }
- const projectId = await getProjectIdFromFile();
- if (!projectId) {
- console.error(MISSING_PROJECT_ID_ERROR);
- process.exit(1);
- }
- return ManagedRemoteAppToken.create(sessionToken, projectId);
-}
-
-function getExpiresFromTtl(ttl: number): Date {
- // ttl is in minutes
- return new Date(Date.now() + ttl * 60 * 1000);
-}
-
-/**
- * Small wrapper around fetch that throws an error if the response is not OK. Allows for custom error handling as well through the onNotOK callback.
- */
-async function safeFetch(
- url: Parameters<typeof fetch>[0],
- options: Parameters<typeof fetch>[1] = {},
- onNotOK: (response: Response) => void | Promise<void> = () => {
- throw new Error(`Request to ${url} returned a non-OK status code.`);
- },
-): Promise<Response> {
- const response = await fetch(url, options);
-
- if (!response.ok) {
- await onNotOK(response);
- }
-
- return response;
-}
diff --git a/packages/studio/src/core/utils.ts b/packages/studio/src/core/utils.ts
deleted file mode 100644
index 7cf40f751..000000000
--- a/packages/studio/src/core/utils.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { loadEnv } from 'vite';
-
-export function getAstroStudioEnv(envMode = ''): Record<`ASTRO_STUDIO_${string}`, string> {
- const env = loadEnv(envMode, process.cwd(), 'ASTRO_STUDIO_');
- return env;
-}
-
-export function getAstroStudioUrl(): string {
- const env = getAstroStudioEnv();
- return env.ASTRO_STUDIO_URL || 'https://studio.astro.build';
-}
diff --git a/packages/studio/src/index.ts b/packages/studio/src/index.ts
deleted file mode 100644
index e97d53dfc..000000000
--- a/packages/studio/src/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './core/tokens.js';
-export * from './core/utils.js';
-export * from './core/errors.js';
diff --git a/packages/studio/tsconfig.json b/packages/studio/tsconfig.json
deleted file mode 100644
index 18443cddf..000000000
--- a/packages/studio/tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extends": "../../tsconfig.base.json",
- "include": ["src"],
- "compilerOptions": {
- "outDir": "./dist"
- }
-}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2a0fabafe..2d54f0da2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4371,9 +4371,6 @@ importers:
packages/db:
dependencies:
- '@astrojs/studio':
- specifier: workspace:*
- version: link:../studio
'@libsql/client':
specifier: ^0.15.2
version: 0.15.2
@@ -6198,28 +6195,6 @@ importers:
specifier: ^2.0.1
version: 2.0.1
- packages/studio:
- dependencies:
- ci-info:
- specifier: ^4.2.0
- version: 4.2.0
- kleur:
- specifier: ^4.1.5
- version: 4.1.5
- yocto-spinner:
- specifier: ^0.2.1
- version: 0.2.1
- devDependencies:
- astro-scripts:
- specifier: workspace:*
- version: link:../../scripts
- typescript:
- specifier: ^5.8.3
- version: 5.8.3
- vite:
- specifier: ^6.3.4
- version: 6.3.5(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.86.3)(yaml@2.5.1)
-
packages/telemetry:
dependencies:
ci-info:
@@ -10694,6 +10669,7 @@ packages:
libsql@0.5.4:
resolution: {integrity: sha512-GEFeWca4SDAQFxjHWJBE6GK52LEtSskiujbG3rqmmeTO9t4sfSBKIURNLLpKDDF7fb7jmTuuRkDAn9BZGITQNw==}
+ cpu: [x64, arm64, wasm32]
os: [darwin, linux, win32]
lightningcss-darwin-arm64@1.29.2: