@robodev-ai/sdk 0.2.0

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.
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@robodev-ai/sdk",
3
+ "version": "0.2.0",
4
+ "description": "Robodev SDK — defineDatabase, defineApi, and the injected Drizzle db",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/robodev-com/robodev-starbase.git",
10
+ "directory": "robodev-sdk"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./src/index.ts",
15
+ "import": "./src/index.ts",
16
+ "default": "./src/index.ts"
17
+ }
18
+ },
19
+ "files": [
20
+ "src"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "dependencies": {
26
+ "drizzle-orm": "^0.45.2",
27
+ "pg": "^8.16.3",
28
+ "zod": "^3.25.76"
29
+ },
30
+ "devDependencies": {
31
+ "@types/pg": "^8.15.5",
32
+ "typescript": "^5.9.2"
33
+ }
34
+ }
package/src/api.ts ADDED
@@ -0,0 +1,71 @@
1
+ import { z } from "zod";
2
+ import type { RobodevDb } from "./db.js";
3
+
4
+ /** Marker written onto every `defineApi(...)` result so Starbase can register the handler. */
5
+ export const API_KIND = "robodevApi";
6
+
7
+ /** Injected into every handler: tenant `db`, parsed query/body, and request headers. */
8
+ export type ApiHandlerContext<TQuery, TBody> = {
9
+ db: RobodevDb;
10
+ query: TQuery;
11
+ body: TBody;
12
+ headers: Record<string, string | string[] | undefined>;
13
+ };
14
+
15
+ export type ApiDefinition<TQuery = unknown, TBody = unknown, TResponse = unknown> = {
16
+ _kind: typeof API_KIND;
17
+ query?: z.ZodType<TQuery>;
18
+ body?: z.ZodType<TBody>;
19
+ response?: z.ZodType<TResponse>;
20
+ handler: (ctx: ApiHandlerContext<TQuery, TBody>) => Promise<TResponse> | TResponse;
21
+ };
22
+
23
+ /**
24
+ * Defines one HTTP handler. Export as `get` / `post` / `put` / `patch` / `delete`
25
+ * from `api/<name>.ts` to serve that method at `/<name>`.
26
+ */
27
+ export function defineApi<
28
+ TQuery extends z.ZodTypeAny | undefined = undefined,
29
+ TBody extends z.ZodTypeAny | undefined = undefined,
30
+ TResponse extends z.ZodTypeAny | undefined = undefined,
31
+ >(config: {
32
+ query?: TQuery;
33
+ body?: TBody;
34
+ response?: TResponse;
35
+ handler: (
36
+ ctx: ApiHandlerContext<
37
+ TQuery extends z.ZodTypeAny ? z.infer<TQuery> : Record<string, never>,
38
+ TBody extends z.ZodTypeAny ? z.infer<TBody> : unknown
39
+ >,
40
+ ) =>
41
+ | Promise<TResponse extends z.ZodTypeAny ? z.infer<TResponse> : unknown>
42
+ | (TResponse extends z.ZodTypeAny ? z.infer<TResponse> : unknown);
43
+ }): ApiDefinition<
44
+ TQuery extends z.ZodTypeAny ? z.infer<TQuery> : Record<string, never>,
45
+ TBody extends z.ZodTypeAny ? z.infer<TBody> : unknown,
46
+ TResponse extends z.ZodTypeAny ? z.infer<TResponse> : unknown
47
+ > {
48
+ return {
49
+ _kind: API_KIND,
50
+ query: config.query,
51
+ body: config.body,
52
+ response: config.response,
53
+ handler: config.handler,
54
+ } as ApiDefinition<
55
+ TQuery extends z.ZodTypeAny ? z.infer<TQuery> : Record<string, never>,
56
+ TBody extends z.ZodTypeAny ? z.infer<TBody> : unknown,
57
+ TResponse extends z.ZodTypeAny ? z.infer<TResponse> : unknown
58
+ >;
59
+ }
60
+
61
+ export function isApiDefinition(value: unknown): value is ApiDefinition {
62
+ return (
63
+ typeof value === "object" &&
64
+ value !== null &&
65
+ (value as ApiDefinition)._kind === API_KIND &&
66
+ typeof (value as ApiDefinition).handler === "function"
67
+ );
68
+ }
69
+
70
+ export const HTTP_METHODS = ["get", "post", "put", "patch", "delete"] as const;
71
+ export type HttpMethod = (typeof HTTP_METHODS)[number];
@@ -0,0 +1,46 @@
1
+ import { is } from "drizzle-orm";
2
+ import { PgTable } from "drizzle-orm/pg-core";
3
+
4
+ /** Marker written onto every `defineDatabase(...)` result so Starbase can find the schema. */
5
+ export const DATABASE_KIND = "robodevDatabase";
6
+
7
+ export type DatabaseDef<TTables extends Record<string, unknown> = Record<string, unknown>> = {
8
+ _kind: typeof DATABASE_KIND;
9
+ name: string;
10
+ tables: TTables;
11
+ };
12
+
13
+ /**
14
+ * Declares a named Postgres database and its Drizzle tables.
15
+ * Default-export this from `database.ts`; `robodev deploy` creates/syncs it (no migration files).
16
+ */
17
+ export function defineDatabase<TTables extends Record<string, unknown>>(config: {
18
+ name: string;
19
+ tables: TTables;
20
+ }): DatabaseDef<TTables> {
21
+ if (!config.name || !/^[a-z][a-z0-9_]{0,47}$/.test(config.name)) {
22
+ throw new Error(
23
+ `Database name "${config.name}" must be lowercase, start with a letter, and use only a-z, 0-9, _`,
24
+ );
25
+ }
26
+ const tables = Object.entries(config.tables).filter(([, value]) => is(value, PgTable));
27
+ if (tables.length === 0) {
28
+ throw new Error("defineDatabase requires at least one pgTable(...)");
29
+ }
30
+ return {
31
+ _kind: DATABASE_KIND,
32
+ name: config.name,
33
+ tables: config.tables,
34
+ };
35
+ }
36
+
37
+ export function isDatabaseDef(value: unknown): value is DatabaseDef {
38
+ return (
39
+ typeof value === "object" &&
40
+ value !== null &&
41
+ (value as DatabaseDef)._kind === DATABASE_KIND &&
42
+ typeof (value as DatabaseDef).name === "string" &&
43
+ typeof (value as DatabaseDef).tables === "object" &&
44
+ (value as DatabaseDef).tables !== null
45
+ );
46
+ }
package/src/db.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { drizzle, type NodePgDatabase } from "drizzle-orm/node-postgres";
2
+ import pg from "pg";
3
+
4
+ export type RobodevDb<TSchema extends Record<string, unknown> = Record<string, unknown>> =
5
+ NodePgDatabase<TSchema>;
6
+
7
+ /** Builds the Drizzle client Starbase injects as `ctx.db`. Used by the runtime, not app code. */
8
+ export function createDb<TSchema extends Record<string, unknown>>(
9
+ connectionString: string,
10
+ schema: TSchema,
11
+ ): { db: RobodevDb<TSchema>; pool: pg.Pool } {
12
+ const pool = new pg.Pool({ connectionString, max: 5 });
13
+ return { db: drizzle(pool, { schema }), pool };
14
+ }
package/src/drizzle.ts ADDED
@@ -0,0 +1,46 @@
1
+ export {
2
+ and,
3
+ asc,
4
+ between,
5
+ desc,
6
+ eq,
7
+ exists,
8
+ getTableColumns,
9
+ getTableName,
10
+ gt,
11
+ gte,
12
+ ilike,
13
+ inArray,
14
+ isNotNull,
15
+ isNull,
16
+ like,
17
+ lt,
18
+ lte,
19
+ ne,
20
+ not,
21
+ notInArray,
22
+ or,
23
+ relations,
24
+ sql,
25
+ } from "drizzle-orm";
26
+ export type { InferInsertModel, InferSelectModel } from "drizzle-orm";
27
+ export {
28
+ bigint,
29
+ boolean,
30
+ check,
31
+ date,
32
+ index,
33
+ integer,
34
+ jsonb,
35
+ numeric,
36
+ pgEnum,
37
+ pgTable,
38
+ primaryKey,
39
+ serial,
40
+ text,
41
+ timestamp,
42
+ unique,
43
+ uniqueIndex,
44
+ uuid,
45
+ varchar,
46
+ } from "drizzle-orm/pg-core";
package/src/index.ts ADDED
@@ -0,0 +1,58 @@
1
+ /** Public SDK: `defineDatabase`, `defineApi`, Zod, and the Drizzle helpers used in app code. */
2
+ export { z } from "zod";
3
+ export { defineDatabase, isDatabaseDef, DATABASE_KIND, type DatabaseDef } from "./database.js";
4
+ export {
5
+ defineApi,
6
+ isApiDefinition,
7
+ HTTP_METHODS,
8
+ API_KIND,
9
+ type ApiDefinition,
10
+ type ApiHandlerContext,
11
+ type HttpMethod,
12
+ } from "./api.js";
13
+ export { createDb, type RobodevDb } from "./db.js";
14
+ export {
15
+ and,
16
+ asc,
17
+ between,
18
+ bigint,
19
+ boolean,
20
+ check,
21
+ date,
22
+ desc,
23
+ eq,
24
+ exists,
25
+ getTableColumns,
26
+ getTableName,
27
+ gt,
28
+ gte,
29
+ ilike,
30
+ inArray,
31
+ index,
32
+ integer,
33
+ isNotNull,
34
+ isNull,
35
+ jsonb,
36
+ like,
37
+ lt,
38
+ lte,
39
+ ne,
40
+ not,
41
+ notInArray,
42
+ numeric,
43
+ or,
44
+ pgEnum,
45
+ pgTable,
46
+ primaryKey,
47
+ relations,
48
+ serial,
49
+ sql,
50
+ text,
51
+ timestamp,
52
+ unique,
53
+ uniqueIndex,
54
+ uuid,
55
+ varchar,
56
+ type InferInsertModel,
57
+ type InferSelectModel,
58
+ } from "./drizzle.js";