@coduckai/sdk 0.1.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.
Files changed (49) hide show
  1. package/dist/ai/index.cjs +32 -0
  2. package/dist/ai/index.cjs.map +1 -0
  3. package/dist/ai/index.d.cts +14 -0
  4. package/dist/ai/index.d.ts +14 -0
  5. package/dist/ai/index.js +30 -0
  6. package/dist/ai/index.js.map +1 -0
  7. package/dist/auth/index.cjs +139 -0
  8. package/dist/auth/index.cjs.map +1 -0
  9. package/dist/auth/index.d.cts +56 -0
  10. package/dist/auth/index.d.ts +56 -0
  11. package/dist/auth/index.js +132 -0
  12. package/dist/auth/index.js.map +1 -0
  13. package/dist/client/index.cjs +70 -0
  14. package/dist/client/index.cjs.map +1 -0
  15. package/dist/client/index.d.cts +20 -0
  16. package/dist/client/index.d.ts +20 -0
  17. package/dist/client/index.js +57 -0
  18. package/dist/client/index.js.map +1 -0
  19. package/dist/db/index.cjs +126 -0
  20. package/dist/db/index.cjs.map +1 -0
  21. package/dist/db/index.d.cts +62 -0
  22. package/dist/db/index.d.ts +62 -0
  23. package/dist/db/index.js +120 -0
  24. package/dist/db/index.js.map +1 -0
  25. package/dist/email/index.cjs +29 -0
  26. package/dist/email/index.cjs.map +1 -0
  27. package/dist/email/index.d.cts +11 -0
  28. package/dist/email/index.d.ts +11 -0
  29. package/dist/email/index.js +27 -0
  30. package/dist/email/index.js.map +1 -0
  31. package/dist/index.cjs +20 -0
  32. package/dist/index.cjs.map +1 -0
  33. package/dist/index.d.cts +32 -0
  34. package/dist/index.d.ts +32 -0
  35. package/dist/index.js +17 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/payments/index.cjs +32 -0
  38. package/dist/payments/index.cjs.map +1 -0
  39. package/dist/payments/index.d.cts +14 -0
  40. package/dist/payments/index.d.ts +14 -0
  41. package/dist/payments/index.js +30 -0
  42. package/dist/payments/index.js.map +1 -0
  43. package/dist/storage/index.cjs +31 -0
  44. package/dist/storage/index.cjs.map +1 -0
  45. package/dist/storage/index.d.cts +13 -0
  46. package/dist/storage/index.d.ts +13 -0
  47. package/dist/storage/index.js +29 -0
  48. package/dist/storage/index.js.map +1 -0
  49. package/package.json +93 -0
@@ -0,0 +1,57 @@
1
+ import axios from 'axios';
2
+
3
+ // src/client/index.ts
4
+ var TOKEN_KEY = "token";
5
+ function setToken(token) {
6
+ if (typeof localStorage !== "undefined") {
7
+ localStorage.setItem(TOKEN_KEY, token);
8
+ }
9
+ }
10
+ function getToken() {
11
+ if (typeof localStorage !== "undefined") {
12
+ return localStorage.getItem(TOKEN_KEY);
13
+ }
14
+ return null;
15
+ }
16
+ function clearToken() {
17
+ if (typeof localStorage !== "undefined") {
18
+ localStorage.removeItem(TOKEN_KEY);
19
+ }
20
+ }
21
+ function isAuthenticated() {
22
+ return !!getToken();
23
+ }
24
+ function getBaseUrl() {
25
+ if (typeof import.meta !== "undefined" && import.meta.env?.VITE_API_URL) {
26
+ return import.meta.env.VITE_API_URL;
27
+ }
28
+ return "/api";
29
+ }
30
+ var api = axios.create({
31
+ baseURL: getBaseUrl(),
32
+ headers: { "Content-Type": "application/json" }
33
+ });
34
+ api.interceptors.request.use((config) => {
35
+ const token = getToken();
36
+ if (token) {
37
+ config.headers.Authorization = `Bearer ${token}`;
38
+ }
39
+ return config;
40
+ });
41
+ api.interceptors.response.use(
42
+ (response) => response,
43
+ (error) => {
44
+ if (error.response?.status === 401) {
45
+ clearToken();
46
+ if (typeof window !== "undefined") {
47
+ window.location.href = "/login";
48
+ }
49
+ }
50
+ return Promise.reject(error);
51
+ }
52
+ );
53
+ var client_default = api;
54
+
55
+ export { clearToken, client_default as default, getToken, isAuthenticated, setToken };
56
+ //# sourceMappingURL=index.js.map
57
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/client/index.ts"],"names":[],"mappings":";;;AAeA,IAAM,SAAA,GAAY,OAAA;AAEX,SAAS,SAAS,KAAA,EAAqB;AAC5C,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,YAAA,CAAa,OAAA,CAAQ,WAAW,KAAK,CAAA;AAAA,EACvC;AACF;AAEO,SAAS,QAAA,GAA0B;AACxC,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,OAAO,YAAA,CAAa,QAAQ,SAAS,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,IAAA;AACT;AAEO,SAAS,UAAA,GAAmB;AACjC,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,YAAA,CAAa,WAAW,SAAS,CAAA;AAAA,EACnC;AACF;AAEO,SAAS,eAAA,GAA2B;AACzC,EAAA,OAAO,CAAC,CAAC,QAAA,EAAS;AACpB;AAIA,SAAS,UAAA,GAAqB;AAE5B,EAAA,IAAI,OAAO,MAAA,CAAA,IAAA,KAAgB,WAAA,IAAgB,MAAA,CAAA,IAAA,CAAoB,KAAK,YAAA,EAAc;AAChF,IAAA,OAAQ,YAAoB,GAAA,CAAI,YAAA;AAAA,EAClC;AAEA,EAAA,OAAO,MAAA;AACT;AAEA,IAAM,GAAA,GAAqB,MAAM,MAAA,CAAO;AAAA,EACtC,SAAS,UAAA,EAAW;AAAA,EACpB,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAC7B,CAAC,CAAA;AAGD,GAAA,CAAI,YAAA,CAAa,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAW;AACvC,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAA,CAAO,OAAA,CAAQ,aAAA,GAAgB,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA;AAAA,EAChD;AACA,EAAA,OAAO,MAAA;AACT,CAAC,CAAA;AAGD,GAAA,CAAI,aAAa,QAAA,CAAS,GAAA;AAAA,EACxB,CAAC,QAAA,KAAa,QAAA;AAAA,EACd,CAAC,KAAA,KAAU;AACT,IAAA,IAAI,KAAA,CAAM,QAAA,EAAU,MAAA,KAAW,GAAA,EAAK;AAClC,MAAA,UAAA,EAAW;AACX,MAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,QAAA,MAAA,CAAO,SAAS,IAAA,GAAO,QAAA;AAAA,MACzB;AAAA,IACF;AACA,IAAA,OAAO,OAAA,CAAQ,OAAO,KAAK,CAAA;AAAA,EAC7B;AACF,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"index.js","sourcesContent":["/**\n * @coduckai/sdk/client — Frontend API Client (browser-only)\n *\n * Pre-configured Axios instance with auth interceptor.\n *\n * Usage:\n * import api, { setToken, clearToken } from '@coduckai/sdk/client';\n * const { data } = await api.get('/recipes');\n * setToken(loginData.token);\n */\n\nimport axios, { type AxiosInstance } from 'axios';\n\n// ── Token Management ───────────────────────────────────\n\nconst TOKEN_KEY = 'token';\n\nexport function setToken(token: string): void {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(TOKEN_KEY, token);\n }\n}\n\nexport function getToken(): string | null {\n if (typeof localStorage !== 'undefined') {\n return localStorage.getItem(TOKEN_KEY);\n }\n return null;\n}\n\nexport function clearToken(): void {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem(TOKEN_KEY);\n }\n}\n\nexport function isAuthenticated(): boolean {\n return !!getToken();\n}\n\n// ── Axios Instance ─────────────────────────────────────\n\nfunction getBaseUrl(): string {\n // Vite: VITE_API_URL\n if (typeof import.meta !== 'undefined' && (import.meta as any).env?.VITE_API_URL) {\n return (import.meta as any).env.VITE_API_URL;\n }\n // Fallback: relative /api\n return '/api';\n}\n\nconst api: AxiosInstance = axios.create({\n baseURL: getBaseUrl(),\n headers: { 'Content-Type': 'application/json' },\n});\n\n// Request interceptor — attach Bearer token\napi.interceptors.request.use((config) => {\n const token = getToken();\n if (token) {\n config.headers.Authorization = `Bearer ${token}`;\n }\n return config;\n});\n\n// Response interceptor — handle 401\napi.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.response?.status === 401) {\n clearToken();\n if (typeof window !== 'undefined') {\n window.location.href = '/login';\n }\n }\n return Promise.reject(error);\n }\n);\n\nexport default api;\n"]}
@@ -0,0 +1,126 @@
1
+ 'use strict';
2
+
3
+ var pg = require('pg');
4
+
5
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
+
7
+ var pg__default = /*#__PURE__*/_interopDefault(pg);
8
+
9
+ // src/internal/env.ts
10
+ function getEnv(key, fallback) {
11
+ return process.env[key] || fallback;
12
+ }
13
+ function isServer() {
14
+ return typeof window === "undefined";
15
+ }
16
+ function assertServer(moduleName) {
17
+ if (!isServer()) {
18
+ throw new Error(
19
+ `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`
20
+ );
21
+ }
22
+ }
23
+
24
+ // src/internal/config.ts
25
+ var globalConfig = {};
26
+ function getConfig() {
27
+ return globalConfig;
28
+ }
29
+ assertServer("db");
30
+ var { Pool } = pg__default.default;
31
+ var pool = null;
32
+ function getPool() {
33
+ if (!pool) {
34
+ const config = getConfig();
35
+ const connectionString = config.db?.connectionString || getEnv("DATABASE_URL");
36
+ if (!connectionString) {
37
+ throw new Error("@coduckai/sdk/db: DATABASE_URL environment variable is required");
38
+ }
39
+ pool = new Pool({ connectionString });
40
+ }
41
+ return pool;
42
+ }
43
+ var db = {
44
+ /**
45
+ * Run a query, returns all rows
46
+ */
47
+ async query(sql, params) {
48
+ const result = await getPool().query(sql, params);
49
+ return result.rows;
50
+ },
51
+ /**
52
+ * Run a query, returns the first row or null
53
+ */
54
+ async queryOne(sql, params) {
55
+ const result = await getPool().query(sql, params);
56
+ return result.rows[0] || null;
57
+ },
58
+ /**
59
+ * Run a query that doesn't return rows (INSERT/UPDATE/DELETE)
60
+ */
61
+ async execute(sql, params) {
62
+ const result = await getPool().query(sql, params);
63
+ return { rowCount: result.rowCount ?? 0 };
64
+ },
65
+ /**
66
+ * Paginated query — automatically appends LIMIT/OFFSET and runs COUNT
67
+ */
68
+ async paginate(sql, params, options) {
69
+ const { page, pageSize } = options;
70
+ const offset = (page - 1) * pageSize;
71
+ const countSql = `SELECT COUNT(*) as total FROM (${sql}) AS _count_query`;
72
+ const countResult = await getPool().query(countSql, params);
73
+ const total = parseInt(countResult.rows[0].total, 10);
74
+ const dataSql = `${sql} LIMIT $${params.length + 1} OFFSET $${params.length + 2}`;
75
+ const dataResult = await getPool().query(dataSql, [...params, pageSize, offset]);
76
+ return {
77
+ data: dataResult.rows,
78
+ total,
79
+ page,
80
+ pageSize,
81
+ totalPages: Math.ceil(total / pageSize)
82
+ };
83
+ },
84
+ /**
85
+ * Run multiple queries in a transaction. Auto-commits on success, rolls back on error.
86
+ */
87
+ async transaction(fn) {
88
+ const client = await getPool().connect();
89
+ try {
90
+ await client.query("BEGIN");
91
+ const txClient = createTransactionClient(client);
92
+ const result = await fn(txClient);
93
+ await client.query("COMMIT");
94
+ return result;
95
+ } catch (error) {
96
+ await client.query("ROLLBACK");
97
+ throw error;
98
+ } finally {
99
+ client.release();
100
+ }
101
+ },
102
+ /**
103
+ * Get the underlying pg Pool (escape hatch)
104
+ */
105
+ getPool
106
+ };
107
+ function createTransactionClient(client) {
108
+ return {
109
+ async query(sql, params) {
110
+ const result = await client.query(sql, params);
111
+ return result.rows;
112
+ },
113
+ async queryOne(sql, params) {
114
+ const result = await client.query(sql, params);
115
+ return result.rows[0] || null;
116
+ },
117
+ async execute(sql, params) {
118
+ const result = await client.query(sql, params);
119
+ return { rowCount: result.rowCount ?? 0 };
120
+ }
121
+ };
122
+ }
123
+
124
+ exports.db = db;
125
+ //# sourceMappingURL=index.cjs.map
126
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/env.ts","../../src/internal/config.ts","../../src/db/index.ts"],"names":["pg"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Environment variable helpers\n */\n\nexport function getEnv(key: string, fallback?: string): string | undefined {\n return process.env[key] || fallback;\n}\n\nexport function requireEnv(key: string): string {\n const val = process.env[key];\n if (!val) {\n throw new Error(`@coduckai/sdk: Missing required environment variable: ${key}`);\n }\n return val;\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined';\n}\n\nexport function assertServer(moduleName: string): void {\n if (!isServer()) {\n throw new Error(\n `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`\n );\n }\n}\n","/**\n * Global SDK configuration\n *\n * CoDuck apps use env vars automatically — no configure() needed.\n * Standalone usage can call configure() to override.\n */\n\nexport interface SDKConfig {\n auth?: {\n jwtSecret?: string;\n jwtExpiresIn?: string;\n bcryptRounds?: number;\n };\n db?: {\n connectionString?: string;\n };\n client?: {\n baseUrl?: string;\n tokenKey?: string;\n };\n}\n\nlet globalConfig: SDKConfig = {};\n\nexport function setConfig(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\nexport function getConfig(): SDKConfig {\n return globalConfig;\n}\n","/**\n * @coduckai/sdk/db — Database module (server-only)\n *\n * Wraps pg Pool. Returns rows directly, adds pagination and transactions.\n *\n * Usage:\n * import { db } from '@coduckai/sdk/db';\n * const users = await db.query('SELECT * FROM users WHERE active = $1', [true]);\n * const user = await db.queryOne('SELECT * FROM users WHERE id = $1', [id]);\n */\n\nimport { assertServer, getEnv } from '../internal/env';\nimport { getConfig } from '../internal/config';\n\nassertServer('db');\n\nimport pg from 'pg';\nconst { Pool } = pg;\nimport type { PoolClient, QueryResultRow } from 'pg';\n\n// ── Types ──────────────────────────────────────────────\n\nexport interface PaginationOptions {\n page: number;\n pageSize: number;\n}\n\nexport interface PaginatedResult<T> {\n data: T[];\n total: number;\n page: number;\n pageSize: number;\n totalPages: number;\n}\n\n// ── Pool (lazy singleton) ──────────────────────────────\n\nlet pool: pg.Pool | null = null;\n\nfunction getPool(): pg.Pool {\n if (!pool) {\n const config = getConfig();\n const connectionString = config.db?.connectionString || getEnv('DATABASE_URL');\n if (!connectionString) {\n throw new Error('@coduckai/sdk/db: DATABASE_URL environment variable is required');\n }\n pool = new Pool({ connectionString });\n }\n return pool;\n}\n\n// ── DB API ─────────────────────────────────────────────\n\nexport const db = {\n /**\n * Run a query, returns all rows\n */\n async query<T extends QueryResultRow = QueryResultRow>(\n sql: string,\n params?: unknown[]\n ): Promise<T[]> {\n const result = await getPool().query<T>(sql, params);\n return result.rows;\n },\n\n /**\n * Run a query, returns the first row or null\n */\n async queryOne<T extends QueryResultRow = QueryResultRow>(\n sql: string,\n params?: unknown[]\n ): Promise<T | null> {\n const result = await getPool().query<T>(sql, params);\n return result.rows[0] || null;\n },\n\n /**\n * Run a query that doesn't return rows (INSERT/UPDATE/DELETE)\n */\n async execute(\n sql: string,\n params?: unknown[]\n ): Promise<{ rowCount: number }> {\n const result = await getPool().query(sql, params);\n return { rowCount: result.rowCount ?? 0 };\n },\n\n /**\n * Paginated query — automatically appends LIMIT/OFFSET and runs COUNT\n */\n async paginate<T extends QueryResultRow = QueryResultRow>(\n sql: string,\n params: unknown[],\n options: PaginationOptions\n ): Promise<PaginatedResult<T>> {\n const { page, pageSize } = options;\n const offset = (page - 1) * pageSize;\n\n // Count query — wrap the user's SQL\n const countSql = `SELECT COUNT(*) as total FROM (${sql}) AS _count_query`;\n const countResult = await getPool().query(countSql, params);\n const total = parseInt(countResult.rows[0].total, 10);\n\n // Data query — append LIMIT/OFFSET\n const dataSql = `${sql} LIMIT $${params.length + 1} OFFSET $${params.length + 2}`;\n const dataResult = await getPool().query<T>(dataSql, [...params, pageSize, offset]);\n\n return {\n data: dataResult.rows,\n total,\n page,\n pageSize,\n totalPages: Math.ceil(total / pageSize),\n };\n },\n\n /**\n * Run multiple queries in a transaction. Auto-commits on success, rolls back on error.\n */\n async transaction<T>(fn: (tx: TransactionClient) => Promise<T>): Promise<T> {\n const client = await getPool().connect();\n try {\n await client.query('BEGIN');\n const txClient = createTransactionClient(client);\n const result = await fn(txClient);\n await client.query('COMMIT');\n return result;\n } catch (error) {\n await client.query('ROLLBACK');\n throw error;\n } finally {\n client.release();\n }\n },\n\n /**\n * Get the underlying pg Pool (escape hatch)\n */\n getPool,\n};\n\n// ── Transaction Client ─────────────────────────────────\n\nexport interface TransactionClient {\n query<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T[]>;\n queryOne<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T | null>;\n execute(sql: string, params?: unknown[]): Promise<{ rowCount: number }>;\n}\n\nfunction createTransactionClient(client: PoolClient): TransactionClient {\n return {\n async query<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T[]> {\n const result = await client.query<T>(sql, params);\n return result.rows;\n },\n async queryOne<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T | null> {\n const result = await client.query<T>(sql, params);\n return result.rows[0] || null;\n },\n async execute(sql: string, params?: unknown[]): Promise<{ rowCount: number }> {\n const result = await client.query(sql, params);\n return { rowCount: result.rowCount ?? 0 };\n },\n };\n}\n"]}
@@ -0,0 +1,62 @@
1
+ import pg, { QueryResultRow } from 'pg';
2
+
3
+ /**
4
+ * @coduckai/sdk/db — Database module (server-only)
5
+ *
6
+ * Wraps pg Pool. Returns rows directly, adds pagination and transactions.
7
+ *
8
+ * Usage:
9
+ * import { db } from '@coduckai/sdk/db';
10
+ * const users = await db.query('SELECT * FROM users WHERE active = $1', [true]);
11
+ * const user = await db.queryOne('SELECT * FROM users WHERE id = $1', [id]);
12
+ */
13
+
14
+ interface PaginationOptions {
15
+ page: number;
16
+ pageSize: number;
17
+ }
18
+ interface PaginatedResult<T> {
19
+ data: T[];
20
+ total: number;
21
+ page: number;
22
+ pageSize: number;
23
+ totalPages: number;
24
+ }
25
+ declare function getPool(): pg.Pool;
26
+ declare const db: {
27
+ /**
28
+ * Run a query, returns all rows
29
+ */
30
+ query<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T[]>;
31
+ /**
32
+ * Run a query, returns the first row or null
33
+ */
34
+ queryOne<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T | null>;
35
+ /**
36
+ * Run a query that doesn't return rows (INSERT/UPDATE/DELETE)
37
+ */
38
+ execute(sql: string, params?: unknown[]): Promise<{
39
+ rowCount: number;
40
+ }>;
41
+ /**
42
+ * Paginated query — automatically appends LIMIT/OFFSET and runs COUNT
43
+ */
44
+ paginate<T extends QueryResultRow = QueryResultRow>(sql: string, params: unknown[], options: PaginationOptions): Promise<PaginatedResult<T>>;
45
+ /**
46
+ * Run multiple queries in a transaction. Auto-commits on success, rolls back on error.
47
+ */
48
+ transaction<T>(fn: (tx: TransactionClient) => Promise<T>): Promise<T>;
49
+ /**
50
+ * Get the underlying pg Pool (escape hatch)
51
+ */
52
+ getPool: typeof getPool;
53
+ };
54
+ interface TransactionClient {
55
+ query<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T[]>;
56
+ queryOne<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T | null>;
57
+ execute(sql: string, params?: unknown[]): Promise<{
58
+ rowCount: number;
59
+ }>;
60
+ }
61
+
62
+ export { type PaginatedResult, type PaginationOptions, type TransactionClient, db };
@@ -0,0 +1,62 @@
1
+ import pg, { QueryResultRow } from 'pg';
2
+
3
+ /**
4
+ * @coduckai/sdk/db — Database module (server-only)
5
+ *
6
+ * Wraps pg Pool. Returns rows directly, adds pagination and transactions.
7
+ *
8
+ * Usage:
9
+ * import { db } from '@coduckai/sdk/db';
10
+ * const users = await db.query('SELECT * FROM users WHERE active = $1', [true]);
11
+ * const user = await db.queryOne('SELECT * FROM users WHERE id = $1', [id]);
12
+ */
13
+
14
+ interface PaginationOptions {
15
+ page: number;
16
+ pageSize: number;
17
+ }
18
+ interface PaginatedResult<T> {
19
+ data: T[];
20
+ total: number;
21
+ page: number;
22
+ pageSize: number;
23
+ totalPages: number;
24
+ }
25
+ declare function getPool(): pg.Pool;
26
+ declare const db: {
27
+ /**
28
+ * Run a query, returns all rows
29
+ */
30
+ query<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T[]>;
31
+ /**
32
+ * Run a query, returns the first row or null
33
+ */
34
+ queryOne<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T | null>;
35
+ /**
36
+ * Run a query that doesn't return rows (INSERT/UPDATE/DELETE)
37
+ */
38
+ execute(sql: string, params?: unknown[]): Promise<{
39
+ rowCount: number;
40
+ }>;
41
+ /**
42
+ * Paginated query — automatically appends LIMIT/OFFSET and runs COUNT
43
+ */
44
+ paginate<T extends QueryResultRow = QueryResultRow>(sql: string, params: unknown[], options: PaginationOptions): Promise<PaginatedResult<T>>;
45
+ /**
46
+ * Run multiple queries in a transaction. Auto-commits on success, rolls back on error.
47
+ */
48
+ transaction<T>(fn: (tx: TransactionClient) => Promise<T>): Promise<T>;
49
+ /**
50
+ * Get the underlying pg Pool (escape hatch)
51
+ */
52
+ getPool: typeof getPool;
53
+ };
54
+ interface TransactionClient {
55
+ query<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T[]>;
56
+ queryOne<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T | null>;
57
+ execute(sql: string, params?: unknown[]): Promise<{
58
+ rowCount: number;
59
+ }>;
60
+ }
61
+
62
+ export { type PaginatedResult, type PaginationOptions, type TransactionClient, db };
@@ -0,0 +1,120 @@
1
+ import pg from 'pg';
2
+
3
+ // src/internal/env.ts
4
+ function getEnv(key, fallback) {
5
+ return process.env[key] || fallback;
6
+ }
7
+ function isServer() {
8
+ return typeof window === "undefined";
9
+ }
10
+ function assertServer(moduleName) {
11
+ if (!isServer()) {
12
+ throw new Error(
13
+ `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`
14
+ );
15
+ }
16
+ }
17
+
18
+ // src/internal/config.ts
19
+ var globalConfig = {};
20
+ function getConfig() {
21
+ return globalConfig;
22
+ }
23
+ assertServer("db");
24
+ var { Pool } = pg;
25
+ var pool = null;
26
+ function getPool() {
27
+ if (!pool) {
28
+ const config = getConfig();
29
+ const connectionString = config.db?.connectionString || getEnv("DATABASE_URL");
30
+ if (!connectionString) {
31
+ throw new Error("@coduckai/sdk/db: DATABASE_URL environment variable is required");
32
+ }
33
+ pool = new Pool({ connectionString });
34
+ }
35
+ return pool;
36
+ }
37
+ var db = {
38
+ /**
39
+ * Run a query, returns all rows
40
+ */
41
+ async query(sql, params) {
42
+ const result = await getPool().query(sql, params);
43
+ return result.rows;
44
+ },
45
+ /**
46
+ * Run a query, returns the first row or null
47
+ */
48
+ async queryOne(sql, params) {
49
+ const result = await getPool().query(sql, params);
50
+ return result.rows[0] || null;
51
+ },
52
+ /**
53
+ * Run a query that doesn't return rows (INSERT/UPDATE/DELETE)
54
+ */
55
+ async execute(sql, params) {
56
+ const result = await getPool().query(sql, params);
57
+ return { rowCount: result.rowCount ?? 0 };
58
+ },
59
+ /**
60
+ * Paginated query — automatically appends LIMIT/OFFSET and runs COUNT
61
+ */
62
+ async paginate(sql, params, options) {
63
+ const { page, pageSize } = options;
64
+ const offset = (page - 1) * pageSize;
65
+ const countSql = `SELECT COUNT(*) as total FROM (${sql}) AS _count_query`;
66
+ const countResult = await getPool().query(countSql, params);
67
+ const total = parseInt(countResult.rows[0].total, 10);
68
+ const dataSql = `${sql} LIMIT $${params.length + 1} OFFSET $${params.length + 2}`;
69
+ const dataResult = await getPool().query(dataSql, [...params, pageSize, offset]);
70
+ return {
71
+ data: dataResult.rows,
72
+ total,
73
+ page,
74
+ pageSize,
75
+ totalPages: Math.ceil(total / pageSize)
76
+ };
77
+ },
78
+ /**
79
+ * Run multiple queries in a transaction. Auto-commits on success, rolls back on error.
80
+ */
81
+ async transaction(fn) {
82
+ const client = await getPool().connect();
83
+ try {
84
+ await client.query("BEGIN");
85
+ const txClient = createTransactionClient(client);
86
+ const result = await fn(txClient);
87
+ await client.query("COMMIT");
88
+ return result;
89
+ } catch (error) {
90
+ await client.query("ROLLBACK");
91
+ throw error;
92
+ } finally {
93
+ client.release();
94
+ }
95
+ },
96
+ /**
97
+ * Get the underlying pg Pool (escape hatch)
98
+ */
99
+ getPool
100
+ };
101
+ function createTransactionClient(client) {
102
+ return {
103
+ async query(sql, params) {
104
+ const result = await client.query(sql, params);
105
+ return result.rows;
106
+ },
107
+ async queryOne(sql, params) {
108
+ const result = await client.query(sql, params);
109
+ return result.rows[0] || null;
110
+ },
111
+ async execute(sql, params) {
112
+ const result = await client.query(sql, params);
113
+ return { rowCount: result.rowCount ?? 0 };
114
+ }
115
+ };
116
+ }
117
+
118
+ export { db };
119
+ //# sourceMappingURL=index.js.map
120
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/env.ts","../../src/internal/config.ts","../../src/db/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Environment variable helpers\n */\n\nexport function getEnv(key: string, fallback?: string): string | undefined {\n return process.env[key] || fallback;\n}\n\nexport function requireEnv(key: string): string {\n const val = process.env[key];\n if (!val) {\n throw new Error(`@coduckai/sdk: Missing required environment variable: ${key}`);\n }\n return val;\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined';\n}\n\nexport function assertServer(moduleName: string): void {\n if (!isServer()) {\n throw new Error(\n `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`\n );\n }\n}\n","/**\n * Global SDK configuration\n *\n * CoDuck apps use env vars automatically — no configure() needed.\n * Standalone usage can call configure() to override.\n */\n\nexport interface SDKConfig {\n auth?: {\n jwtSecret?: string;\n jwtExpiresIn?: string;\n bcryptRounds?: number;\n };\n db?: {\n connectionString?: string;\n };\n client?: {\n baseUrl?: string;\n tokenKey?: string;\n };\n}\n\nlet globalConfig: SDKConfig = {};\n\nexport function setConfig(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\nexport function getConfig(): SDKConfig {\n return globalConfig;\n}\n","/**\n * @coduckai/sdk/db — Database module (server-only)\n *\n * Wraps pg Pool. Returns rows directly, adds pagination and transactions.\n *\n * Usage:\n * import { db } from '@coduckai/sdk/db';\n * const users = await db.query('SELECT * FROM users WHERE active = $1', [true]);\n * const user = await db.queryOne('SELECT * FROM users WHERE id = $1', [id]);\n */\n\nimport { assertServer, getEnv } from '../internal/env';\nimport { getConfig } from '../internal/config';\n\nassertServer('db');\n\nimport pg from 'pg';\nconst { Pool } = pg;\nimport type { PoolClient, QueryResultRow } from 'pg';\n\n// ── Types ──────────────────────────────────────────────\n\nexport interface PaginationOptions {\n page: number;\n pageSize: number;\n}\n\nexport interface PaginatedResult<T> {\n data: T[];\n total: number;\n page: number;\n pageSize: number;\n totalPages: number;\n}\n\n// ── Pool (lazy singleton) ──────────────────────────────\n\nlet pool: pg.Pool | null = null;\n\nfunction getPool(): pg.Pool {\n if (!pool) {\n const config = getConfig();\n const connectionString = config.db?.connectionString || getEnv('DATABASE_URL');\n if (!connectionString) {\n throw new Error('@coduckai/sdk/db: DATABASE_URL environment variable is required');\n }\n pool = new Pool({ connectionString });\n }\n return pool;\n}\n\n// ── DB API ─────────────────────────────────────────────\n\nexport const db = {\n /**\n * Run a query, returns all rows\n */\n async query<T extends QueryResultRow = QueryResultRow>(\n sql: string,\n params?: unknown[]\n ): Promise<T[]> {\n const result = await getPool().query<T>(sql, params);\n return result.rows;\n },\n\n /**\n * Run a query, returns the first row or null\n */\n async queryOne<T extends QueryResultRow = QueryResultRow>(\n sql: string,\n params?: unknown[]\n ): Promise<T | null> {\n const result = await getPool().query<T>(sql, params);\n return result.rows[0] || null;\n },\n\n /**\n * Run a query that doesn't return rows (INSERT/UPDATE/DELETE)\n */\n async execute(\n sql: string,\n params?: unknown[]\n ): Promise<{ rowCount: number }> {\n const result = await getPool().query(sql, params);\n return { rowCount: result.rowCount ?? 0 };\n },\n\n /**\n * Paginated query — automatically appends LIMIT/OFFSET and runs COUNT\n */\n async paginate<T extends QueryResultRow = QueryResultRow>(\n sql: string,\n params: unknown[],\n options: PaginationOptions\n ): Promise<PaginatedResult<T>> {\n const { page, pageSize } = options;\n const offset = (page - 1) * pageSize;\n\n // Count query — wrap the user's SQL\n const countSql = `SELECT COUNT(*) as total FROM (${sql}) AS _count_query`;\n const countResult = await getPool().query(countSql, params);\n const total = parseInt(countResult.rows[0].total, 10);\n\n // Data query — append LIMIT/OFFSET\n const dataSql = `${sql} LIMIT $${params.length + 1} OFFSET $${params.length + 2}`;\n const dataResult = await getPool().query<T>(dataSql, [...params, pageSize, offset]);\n\n return {\n data: dataResult.rows,\n total,\n page,\n pageSize,\n totalPages: Math.ceil(total / pageSize),\n };\n },\n\n /**\n * Run multiple queries in a transaction. Auto-commits on success, rolls back on error.\n */\n async transaction<T>(fn: (tx: TransactionClient) => Promise<T>): Promise<T> {\n const client = await getPool().connect();\n try {\n await client.query('BEGIN');\n const txClient = createTransactionClient(client);\n const result = await fn(txClient);\n await client.query('COMMIT');\n return result;\n } catch (error) {\n await client.query('ROLLBACK');\n throw error;\n } finally {\n client.release();\n }\n },\n\n /**\n * Get the underlying pg Pool (escape hatch)\n */\n getPool,\n};\n\n// ── Transaction Client ─────────────────────────────────\n\nexport interface TransactionClient {\n query<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T[]>;\n queryOne<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T | null>;\n execute(sql: string, params?: unknown[]): Promise<{ rowCount: number }>;\n}\n\nfunction createTransactionClient(client: PoolClient): TransactionClient {\n return {\n async query<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T[]> {\n const result = await client.query<T>(sql, params);\n return result.rows;\n },\n async queryOne<T extends QueryResultRow = QueryResultRow>(sql: string, params?: unknown[]): Promise<T | null> {\n const result = await client.query<T>(sql, params);\n return result.rows[0] || null;\n },\n async execute(sql: string, params?: unknown[]): Promise<{ rowCount: number }> {\n const result = await client.query(sql, params);\n return { rowCount: result.rowCount ?? 0 };\n },\n };\n}\n"]}
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ // src/internal/env.ts
4
+ function isServer() {
5
+ return typeof window === "undefined";
6
+ }
7
+ function assertServer(moduleName) {
8
+ if (!isServer()) {
9
+ throw new Error(
10
+ `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`
11
+ );
12
+ }
13
+ }
14
+
15
+ // src/email/index.ts
16
+ assertServer("email");
17
+ var email = {
18
+ send: notImplemented("email.send"),
19
+ sendTemplate: notImplemented("email.sendTemplate")
20
+ };
21
+ function notImplemented(name) {
22
+ return () => {
23
+ throw new Error(`@coduckai/sdk: ${name}() is not yet implemented. Coming in v0.3.0.`);
24
+ };
25
+ }
26
+
27
+ exports.email = email;
28
+ //# sourceMappingURL=index.cjs.map
29
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/env.ts","../../src/email/index.ts"],"names":[],"mappings":";;;AAgBO,SAAS,QAAA,GAAoB;AAClC,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA;AAC3B;AAEO,SAAS,aAAa,UAAA,EAA0B;AACrD,EAAA,IAAI,CAAC,UAAS,EAAG;AACf,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,iBAAiB,UAAU,CAAA,2DAAA;AAAA,KAC7B;AAAA,EACF;AACF;;;ACnBA,YAAA,CAAa,OAAO,CAAA;AAEb,IAAM,KAAA,GAAQ;AAAA,EACnB,IAAA,EAAM,eAAe,YAAY,CAAA;AAAA,EACjC,YAAA,EAAc,eAAe,oBAAoB;AACnD;AAEA,SAAS,eAAe,IAAA,EAAc;AACpC,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,eAAA,EAAkB,IAAI,CAAA,4CAAA,CAA8C,CAAA;AAAA,EACtF,CAAA;AACF","file":"index.cjs","sourcesContent":["/**\n * Environment variable helpers\n */\n\nexport function getEnv(key: string, fallback?: string): string | undefined {\n return process.env[key] || fallback;\n}\n\nexport function requireEnv(key: string): string {\n const val = process.env[key];\n if (!val) {\n throw new Error(`@coduckai/sdk: Missing required environment variable: ${key}`);\n }\n return val;\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined';\n}\n\nexport function assertServer(moduleName: string): void {\n if (!isServer()) {\n throw new Error(\n `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`\n );\n }\n}\n","/**\n * @coduckai/sdk/email — Email via Resend (server-only)\n *\n * Coming in v0.3.0.\n */\n\nimport { assertServer } from '../internal/env';\nassertServer('email');\n\nexport const email = {\n send: notImplemented('email.send'),\n sendTemplate: notImplemented('email.sendTemplate'),\n};\n\nfunction notImplemented(name: string) {\n return () => {\n throw new Error(`@coduckai/sdk: ${name}() is not yet implemented. Coming in v0.3.0.`);\n };\n}\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @coduckai/sdk/email — Email via Resend (server-only)
3
+ *
4
+ * Coming in v0.3.0.
5
+ */
6
+ declare const email: {
7
+ send: () => never;
8
+ sendTemplate: () => never;
9
+ };
10
+
11
+ export { email };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @coduckai/sdk/email — Email via Resend (server-only)
3
+ *
4
+ * Coming in v0.3.0.
5
+ */
6
+ declare const email: {
7
+ send: () => never;
8
+ sendTemplate: () => never;
9
+ };
10
+
11
+ export { email };
@@ -0,0 +1,27 @@
1
+ // src/internal/env.ts
2
+ function isServer() {
3
+ return typeof window === "undefined";
4
+ }
5
+ function assertServer(moduleName) {
6
+ if (!isServer()) {
7
+ throw new Error(
8
+ `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`
9
+ );
10
+ }
11
+ }
12
+
13
+ // src/email/index.ts
14
+ assertServer("email");
15
+ var email = {
16
+ send: notImplemented("email.send"),
17
+ sendTemplate: notImplemented("email.sendTemplate")
18
+ };
19
+ function notImplemented(name) {
20
+ return () => {
21
+ throw new Error(`@coduckai/sdk: ${name}() is not yet implemented. Coming in v0.3.0.`);
22
+ };
23
+ }
24
+
25
+ export { email };
26
+ //# sourceMappingURL=index.js.map
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/env.ts","../../src/email/index.ts"],"names":[],"mappings":";AAgBO,SAAS,QAAA,GAAoB;AAClC,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA;AAC3B;AAEO,SAAS,aAAa,UAAA,EAA0B;AACrD,EAAA,IAAI,CAAC,UAAS,EAAG;AACf,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,iBAAiB,UAAU,CAAA,2DAAA;AAAA,KAC7B;AAAA,EACF;AACF;;;ACnBA,YAAA,CAAa,OAAO,CAAA;AAEb,IAAM,KAAA,GAAQ;AAAA,EACnB,IAAA,EAAM,eAAe,YAAY,CAAA;AAAA,EACjC,YAAA,EAAc,eAAe,oBAAoB;AACnD;AAEA,SAAS,eAAe,IAAA,EAAc;AACpC,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,eAAA,EAAkB,IAAI,CAAA,4CAAA,CAA8C,CAAA;AAAA,EACtF,CAAA;AACF","file":"index.js","sourcesContent":["/**\n * Environment variable helpers\n */\n\nexport function getEnv(key: string, fallback?: string): string | undefined {\n return process.env[key] || fallback;\n}\n\nexport function requireEnv(key: string): string {\n const val = process.env[key];\n if (!val) {\n throw new Error(`@coduckai/sdk: Missing required environment variable: ${key}`);\n }\n return val;\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined';\n}\n\nexport function assertServer(moduleName: string): void {\n if (!isServer()) {\n throw new Error(\n `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`\n );\n }\n}\n","/**\n * @coduckai/sdk/email — Email via Resend (server-only)\n *\n * Coming in v0.3.0.\n */\n\nimport { assertServer } from '../internal/env';\nassertServer('email');\n\nexport const email = {\n send: notImplemented('email.send'),\n sendTemplate: notImplemented('email.sendTemplate'),\n};\n\nfunction notImplemented(name: string) {\n return () => {\n throw new Error(`@coduckai/sdk: ${name}() is not yet implemented. Coming in v0.3.0.`);\n };\n}\n"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ // src/internal/config.ts
4
+ var globalConfig = {};
5
+ function setConfig(config) {
6
+ globalConfig = { ...globalConfig, ...config };
7
+ }
8
+ function getConfig() {
9
+ return globalConfig;
10
+ }
11
+
12
+ // src/index.ts
13
+ function configure(config) {
14
+ setConfig(config);
15
+ }
16
+
17
+ exports.configure = configure;
18
+ exports.getConfig = getConfig;
19
+ //# sourceMappingURL=index.cjs.map
20
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/config.ts","../src/index.ts"],"names":[],"mappings":";;;AAsBA,IAAI,eAA0B,EAAC;AAExB,SAAS,UAAU,MAAA,EAAyB;AACjD,EAAA,YAAA,GAAe,EAAE,GAAG,YAAA,EAAc,GAAG,MAAA,EAAO;AAC9C;AAEO,SAAS,SAAA,GAAuB;AACrC,EAAA,OAAO,YAAA;AACT;;;ACnBO,SAAS,UAAU,MAAA,EAAyB;AACjD,EAAA,SAAA,CAAU,MAAM,CAAA;AAClB","file":"index.cjs","sourcesContent":["/**\n * Global SDK configuration\n *\n * CoDuck apps use env vars automatically — no configure() needed.\n * Standalone usage can call configure() to override.\n */\n\nexport interface SDKConfig {\n auth?: {\n jwtSecret?: string;\n jwtExpiresIn?: string;\n bcryptRounds?: number;\n };\n db?: {\n connectionString?: string;\n };\n client?: {\n baseUrl?: string;\n tokenKey?: string;\n };\n}\n\nlet globalConfig: SDKConfig = {};\n\nexport function setConfig(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\nexport function getConfig(): SDKConfig {\n return globalConfig;\n}\n","/**\n * @coduckai/sdk — CoDuck Platform SDK\n *\n * Zero-config in CoDuck apps (env vars auto-detected).\n * For standalone usage, call configure() first.\n */\n\nimport { setConfig, getConfig, type SDKConfig } from './internal/config';\n\nexport type { SDKConfig };\n\nexport function configure(config: SDKConfig): void {\n setConfig(config);\n}\n\nexport { getConfig };\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Global SDK configuration
3
+ *
4
+ * CoDuck apps use env vars automatically — no configure() needed.
5
+ * Standalone usage can call configure() to override.
6
+ */
7
+ interface SDKConfig {
8
+ auth?: {
9
+ jwtSecret?: string;
10
+ jwtExpiresIn?: string;
11
+ bcryptRounds?: number;
12
+ };
13
+ db?: {
14
+ connectionString?: string;
15
+ };
16
+ client?: {
17
+ baseUrl?: string;
18
+ tokenKey?: string;
19
+ };
20
+ }
21
+ declare function getConfig(): SDKConfig;
22
+
23
+ /**
24
+ * @coduckai/sdk — CoDuck Platform SDK
25
+ *
26
+ * Zero-config in CoDuck apps (env vars auto-detected).
27
+ * For standalone usage, call configure() first.
28
+ */
29
+
30
+ declare function configure(config: SDKConfig): void;
31
+
32
+ export { type SDKConfig, configure, getConfig };