@astrojs/db 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,7 +27,7 @@ function printHelp({
27
27
  message.push(
28
28
  linebreak(),
29
29
  ` ${bgGreen(black(` ${commandName} `))} ${green(
30
- `v${"0.8.0"}`
30
+ `v${"0.8.2"}`
31
31
  )} ${headline}`
32
32
  );
33
33
  }
@@ -1,12 +1,11 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { mkdir, writeFile } from "node:fs/promises";
3
- import { DB_TYPES_FILE, RUNTIME_CONFIG_IMPORT, RUNTIME_IMPORT } from "../consts.js";
3
+ import { DB_TYPES_FILE, RUNTIME_IMPORT } from "../consts.js";
4
4
  async function typegen({ tables, root }) {
5
5
  const content = `// This file is generated by \`studio sync\`
6
6
  declare module 'astro:db' {
7
7
  export const db: import(${RUNTIME_IMPORT}).SqliteDB;
8
8
  export const dbUrl: string;
9
- export * from ${RUNTIME_CONFIG_IMPORT};
10
9
 
11
10
  ${Object.entries(tables).map(([name, collection]) => generateTableType(name, collection)).join("\n")}
12
11
  }
@@ -578,7 +578,7 @@ export declare const jsonColumnSchema: z.ZodObject<{
578
578
  default?: unknown;
579
579
  };
580
580
  }>;
581
- export declare const columnSchema: z.ZodUnion<[z.ZodObject<{
581
+ export declare const columnSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
582
582
  type: z.ZodLiteral<"boolean">;
583
583
  schema: z.ZodObject<{
584
584
  name: z.ZodOptional<z.ZodString>;
@@ -1379,7 +1379,7 @@ export declare const referenceableColumnSchema: z.ZodUnion<[z.ZodObject<{
1379
1379
  references?: (() => z.input<typeof numberColumnSchema>) | undefined;
1380
1380
  });
1381
1381
  }>]>;
1382
- export declare const columnsSchema: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodObject<{
1382
+ export declare const columnsSchema: z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
1383
1383
  type: z.ZodLiteral<"boolean">;
1384
1384
  schema: z.ZodObject<{
1385
1385
  name: z.ZodOptional<z.ZodString>;
@@ -1871,7 +1871,7 @@ type ForeignKeysOutput = Omit<ForeignKeysInput, 'references'> & {
1871
1871
  references: MaybeArray<Omit<z.output<typeof referenceableColumnSchema>, 'references'>>;
1872
1872
  };
1873
1873
  export declare const tableSchema: z.ZodObject<{
1874
- columns: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodObject<{
1874
+ columns: z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
1875
1875
  type: z.ZodLiteral<"boolean">;
1876
1876
  schema: z.ZodObject<{
1877
1877
  name: z.ZodOptional<z.ZodString>;
@@ -2441,7 +2441,7 @@ export declare const tableSchema: z.ZodObject<{
2441
2441
  deprecated?: boolean | undefined;
2442
2442
  }>;
2443
2443
  export declare const tablesSchema: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodObject<{
2444
- columns: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodObject<{
2444
+ columns: z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
2445
2445
  type: z.ZodLiteral<"boolean">;
2446
2446
  schema: z.ZodObject<{
2447
2447
  name: z.ZodOptional<z.ZodString>;
@@ -3101,7 +3101,7 @@ export declare const tablesSchema: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodOb
3101
3101
  }>, unknown>;
3102
3102
  export declare const dbConfigSchema: z.ZodObject<{
3103
3103
  tables: z.ZodOptional<z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodObject<{
3104
- columns: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodObject<{
3104
+ columns: z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
3105
3105
  type: z.ZodLiteral<"boolean">;
3106
3106
  schema: z.ZodObject<{
3107
3107
  name: z.ZodOptional<z.ZodString>;
@@ -96,7 +96,7 @@ const jsonColumnSchema = z.object({
96
96
  default: z.unknown().optional()
97
97
  })
98
98
  });
99
- const columnSchema = z.union([
99
+ const columnSchema = z.discriminatedUnion("type", [
100
100
  booleanColumnSchema,
101
101
  numberColumnSchema,
102
102
  textColumnSchema,
@@ -3,7 +3,7 @@ import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
5
  import { MISSING_PROJECT_ID_ERROR, MISSING_SESSION_ID_ERROR } from "./errors.js";
6
- import { getAstroStudioEnv, getAstroStudioUrl } from "./utils.js";
6
+ import { getAstroStudioEnv, getAstroStudioUrl, getRemoteDatabaseUrl, safeFetch } from "./utils.js";
7
7
  const SESSION_LOGIN_FILE = pathToFileURL(join(homedir(), ".astro", "session-token"));
8
8
  const PROJECT_ID_FILE = pathToFileURL(join(process.cwd(), ".astro", "link"));
9
9
  class ManagedLocalAppToken {
@@ -19,21 +19,35 @@ class ManagedLocalAppToken {
19
19
  class ManagedRemoteAppToken {
20
20
  token;
21
21
  session;
22
+ region;
22
23
  projectId;
23
24
  ttl;
24
25
  renewTimer;
26
+ static async getRegionCode() {
27
+ const pingResponse = await safeFetch(new URL(`${getRemoteDatabaseUrl()}/ping`));
28
+ const pingResult = await pingResponse.json();
29
+ return pingResult.data.region;
30
+ }
25
31
  static async create(sessionToken, projectId) {
26
- const response = await fetch(new URL(`${getAstroStudioUrl()}/auth/cli/token-create`), {
27
- method: "POST",
28
- headers: new Headers({
29
- Authorization: `Bearer ${sessionToken}`
30
- }),
31
- body: JSON.stringify({ projectId })
32
- });
32
+ const region = await ManagedRemoteAppToken.getRegionCode();
33
+ const response = await safeFetch(
34
+ new URL(`${getAstroStudioUrl()}/auth/cli/token-create`),
35
+ {
36
+ method: "POST",
37
+ headers: new Headers({
38
+ Authorization: `Bearer ${sessionToken}`
39
+ }),
40
+ body: JSON.stringify({ projectId, region })
41
+ },
42
+ (res) => {
43
+ throw new Error(`Failed to create token: ${res.status} ${res.statusText}`);
44
+ }
45
+ );
33
46
  const { token: shortLivedAppToken, ttl } = await response.json();
34
47
  return new ManagedRemoteAppToken({
35
48
  token: shortLivedAppToken,
36
49
  session: sessionToken,
50
+ region,
37
51
  projectId,
38
52
  ttl
39
53
  });
@@ -41,19 +55,26 @@ class ManagedRemoteAppToken {
41
55
  constructor(options) {
42
56
  this.token = options.token;
43
57
  this.session = options.session;
58
+ this.region = options.region;
44
59
  this.projectId = options.projectId;
45
60
  this.ttl = options.ttl;
46
61
  this.renewTimer = setTimeout(() => this.renew(), 1e3 * 60 * 5 / 2);
47
62
  }
48
63
  async fetch(url, body) {
49
- return fetch(`${getAstroStudioUrl()}${url}`, {
50
- method: "POST",
51
- headers: {
52
- Authorization: `Bearer ${this.session}`,
53
- "Content-Type": "application/json"
64
+ return safeFetch(
65
+ `${getAstroStudioUrl()}${url}`,
66
+ {
67
+ method: "POST",
68
+ headers: {
69
+ Authorization: `Bearer ${this.session}`,
70
+ "Content-Type": "application/json"
71
+ },
72
+ body: JSON.stringify({ ...body, region: this.region })
54
73
  },
55
- body: JSON.stringify(body)
56
- });
74
+ () => {
75
+ throw new Error(`Failed to fetch ${url}.`);
76
+ }
77
+ );
57
78
  }
58
79
  async renew() {
59
80
  clearTimeout(this.renewTimer);
@@ -6,3 +6,14 @@ export declare function getRemoteDatabaseUrl(): string;
6
6
  export declare function getAstroStudioUrl(): string;
7
7
  export declare function getDbDirectoryUrl(root: URL | string): URL;
8
8
  export declare function defineDbIntegration(integration: AstroDbIntegration): AstroIntegration;
9
+ /**
10
+ * 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.
11
+ */
12
+ export declare function safeFetch(url: Parameters<typeof fetch>[0], options?: Parameters<typeof fetch>[1], onNotOK?: (response: Response) => void | Promise<void>): Promise<Response>;
13
+ export type Result<T> = {
14
+ success: true;
15
+ data: T;
16
+ } | {
17
+ success: false;
18
+ data: unknown;
19
+ };
@@ -17,10 +17,20 @@ function getDbDirectoryUrl(root) {
17
17
  function defineDbIntegration(integration) {
18
18
  return integration;
19
19
  }
20
+ async function safeFetch(url, options = {}, onNotOK = () => {
21
+ throw new Error(`Request to ${url} returned a non-OK status code.`);
22
+ }) {
23
+ const response = await fetch(url, options);
24
+ if (!response.ok) {
25
+ await onNotOK(response);
26
+ }
27
+ return response;
28
+ }
20
29
  export {
21
30
  defineDbIntegration,
22
31
  getAstroStudioEnv,
23
32
  getAstroStudioUrl,
24
33
  getDbDirectoryUrl,
25
- getRemoteDatabaseUrl
34
+ getRemoteDatabaseUrl,
35
+ safeFetch
26
36
  };
@@ -2,6 +2,7 @@ import { createClient } from "@libsql/client";
2
2
  import { drizzle as drizzleLibsql } from "drizzle-orm/libsql";
3
3
  import { drizzle as drizzleProxy } from "drizzle-orm/sqlite-proxy";
4
4
  import { z } from "zod";
5
+ import { safeFetch } from "../core/utils.js";
5
6
  const isWebContainer = !!process.versions?.webcontainer;
6
7
  function createLocalDatabaseClient({ dbUrl }) {
7
8
  const url = isWebContainer ? "file:content.db" : dbUrl;
@@ -21,21 +22,24 @@ function createRemoteDatabaseClient(appToken, remoteDbURL) {
21
22
  const db = drizzleProxy(
22
23
  async (sql, parameters, method) => {
23
24
  const requestBody = { sql, args: parameters };
24
- const res = await fetch(url, {
25
- method: "POST",
26
- headers: {
27
- Authorization: `Bearer ${appToken}`,
28
- "Content-Type": "application/json"
25
+ const res = await safeFetch(
26
+ url,
27
+ {
28
+ method: "POST",
29
+ headers: {
30
+ Authorization: `Bearer ${appToken}`,
31
+ "Content-Type": "application/json"
32
+ },
33
+ body: JSON.stringify(requestBody)
29
34
  },
30
- body: JSON.stringify(requestBody)
31
- });
32
- if (!res.ok) {
33
- throw new Error(
34
- `Failed to execute query.
35
+ (response) => {
36
+ throw new Error(
37
+ `Failed to execute query.
35
38
  Query: ${sql}
36
- Full error: ${res.status} ${await res.text()}}`
37
- );
38
- }
39
+ Full error: ${response.status} ${response.statusText}`
40
+ );
41
+ }
42
+ );
39
43
  let remoteResult;
40
44
  try {
41
45
  const json = await res.json();
@@ -62,20 +66,23 @@ Full error: Unexpected JSON response. ${e instanceof Error ? e.message : String(
62
66
  },
63
67
  async (queries) => {
64
68
  const stmts = queries.map(({ sql, params }) => ({ sql, args: params }));
65
- const res = await fetch(url, {
66
- method: "POST",
67
- headers: {
68
- Authorization: `Bearer ${appToken}`,
69
- "Content-Type": "application/json"
69
+ const res = await safeFetch(
70
+ url,
71
+ {
72
+ method: "POST",
73
+ headers: {
74
+ Authorization: `Bearer ${appToken}`,
75
+ "Content-Type": "application/json"
76
+ },
77
+ body: JSON.stringify(stmts)
70
78
  },
71
- body: JSON.stringify(stmts)
72
- });
73
- if (!res.ok) {
74
- throw new Error(
75
- `Failed to execute batch queries.
76
- Full error: ${res.status} ${await res.text()}}`
77
- );
78
- }
79
+ (response) => {
80
+ throw new Error(
81
+ `Failed to execute batch queries.
82
+ Full error: ${response.status} ${response.statusText}}`
83
+ );
84
+ }
85
+ );
79
86
  let remoteResults;
80
87
  try {
81
88
  const json = await res.json();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrojs/db",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -80,11 +80,12 @@
80
80
  "mocha": "^10.2.0",
81
81
  "typescript": "^5.2.2",
82
82
  "vite": "^5.1.4",
83
- "astro-scripts": "0.0.14",
84
- "astro": "4.5.2"
83
+ "astro": "4.5.3",
84
+ "astro-scripts": "0.0.14"
85
85
  },
86
86
  "scripts": {
87
- "build": "astro-scripts build \"src/**/*.ts\" && tsc",
87
+ "types:config": "tsc -p ./tsconfig.config-types.json",
88
+ "build": "astro-scripts build \"src/**/*.ts\" && tsc && pnpm types:config",
88
89
  "build:ci": "astro-scripts build \"src/**/*.ts\"",
89
90
  "dev": "astro-scripts dev \"src/**/*.ts\"",
90
91
  "test": "mocha --exit --timeout 20000 \"test/*.js\" \"test/unit/**/*.js\"",
package/virtual.d.ts CHANGED
@@ -1,9 +1,33 @@
1
1
  declare module 'astro:db' {
2
- export const sql: typeof import('./dist/runtime/config.js').sql;
3
- export const NOW: typeof import('./dist/runtime/config.js').NOW;
4
- export const TRUE: typeof import('./dist/runtime/config.js').TRUE;
5
- export const FALSE: typeof import('./dist/runtime/config.js').FALSE;
6
- export const column: typeof import('./dist/runtime/config.js').column;
7
- export const defineDb: typeof import('./dist/runtime/config.js').defineDb;
8
- export const defineTable: typeof import('./dist/runtime/config.js').defineTable;
2
+ type RuntimeConfig = typeof import('./dist/_internal/runtime/config.js');
3
+
4
+ export const sql: RuntimeConfig['sql'];
5
+ export const NOW: RuntimeConfig['NOW'];
6
+ export const TRUE: RuntimeConfig['TRUE'];
7
+ export const FALSE: RuntimeConfig['FALSE'];
8
+ export const column: RuntimeConfig['column'];
9
+ export const defineDb: RuntimeConfig['defineDb'];
10
+ export const defineTable: RuntimeConfig['defineTable'];
11
+
12
+ export const eq: RuntimeConfig['eq'];
13
+ export const gt: RuntimeConfig['gt'];
14
+ export const gte: RuntimeConfig['gte'];
15
+ export const lt: RuntimeConfig['lt'];
16
+ export const lte: RuntimeConfig['lte'];
17
+ export const ne: RuntimeConfig['ne'];
18
+ export const isNull: RuntimeConfig['isNull'];
19
+ export const isNotNull: RuntimeConfig['isNotNull'];
20
+ export const inArray: RuntimeConfig['inArray'];
21
+ export const notInArray: RuntimeConfig['notInArray'];
22
+ export const exists: RuntimeConfig['exists'];
23
+ export const notExists: RuntimeConfig['notExists'];
24
+ export const between: RuntimeConfig['between'];
25
+ export const notBetween: RuntimeConfig['notBetween'];
26
+ export const like: RuntimeConfig['like'];
27
+ export const notIlike: RuntimeConfig['notIlike'];
28
+ export const not: RuntimeConfig['not'];
29
+ export const asc: RuntimeConfig['asc'];
30
+ export const desc: RuntimeConfig['desc'];
31
+ export const and: RuntimeConfig['and'];
32
+ export const or: RuntimeConfig['or'];
9
33
  }