@autoship/react 0.1.0 → 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.
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAgTA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAcvD"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AA6WA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAcvD"}
package/dist/cli/init.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createClient } from "@supabase/supabase-js";
1
+ import postgres from "postgres";
2
2
  import * as fs from "fs";
3
3
  import * as path from "path";
4
4
  import * as readline from "readline";
@@ -32,11 +32,14 @@ function parseArgs(args) {
32
32
  const options = {};
33
33
  for (let i = 0; i < args.length; i++) {
34
34
  const arg = args[i];
35
- if (arg === "--db-url" && args[i + 1]) {
35
+ if (arg === "--database-url" && args[i + 1]) {
36
+ options.databaseUrl = args[++i];
37
+ }
38
+ else if (arg === "--supabase-url" && args[i + 1]) {
36
39
  options.supabaseUrl = args[++i];
37
40
  }
38
- else if (arg === "--service-key" && args[i + 1]) {
39
- options.supabaseServiceKey = args[++i];
41
+ else if (arg === "--db-password" && args[i + 1]) {
42
+ options.dbPassword = args[++i];
40
43
  }
41
44
  else if (arg === "--non-interactive" || arg === "-y") {
42
45
  options.nonInteractive = true;
@@ -56,24 +59,36 @@ Usage:
56
59
  npx autoship init [options]
57
60
 
58
61
  Options:
59
- --db-url <url> Supabase project URL
60
- --service-key <key> Supabase service role key
62
+ --database-url <url> Full PostgreSQL connection URL (includes password)
63
+ --supabase-url <url> Supabase project URL (used to construct DATABASE_URL)
64
+ --db-password <pwd> Database password (used with --supabase-url)
61
65
  -y, --non-interactive Skip confirmation prompts
62
66
  -h, --help Show this help message
63
67
 
64
68
  Environment Variables:
65
- SUPABASE_URL Supabase project URL (fallback)
66
- SUPABASE_SERVICE_KEY Supabase service role key (fallback)
69
+ DATABASE_URL Full PostgreSQL connection URL (preferred)
70
+ SUPABASE_URL Supabase project URL (fallback, requires DB_PASSWORD)
71
+ DB_PASSWORD Database password (used with SUPABASE_URL)
72
+
73
+ Connection Methods:
74
+ 1. Provide DATABASE_URL directly (recommended):
75
+ DATABASE_URL=postgresql://postgres.xxx:[password]@aws-0-region.pooler.supabase.com:6543/postgres
76
+
77
+ 2. Provide SUPABASE_URL + DB_PASSWORD:
78
+ The CLI will construct the DATABASE_URL from your Supabase project URL.
67
79
 
68
80
  Examples:
69
81
  # Interactive mode (prompts for credentials)
70
82
  npx autoship init
71
83
 
72
- # With command line arguments
73
- npx autoship init --db-url https://xxx.supabase.co --service-key eyJ...
84
+ # With full database URL
85
+ npx autoship init --database-url "postgresql://postgres.xxx:password@aws-0-region.pooler.supabase.com:6543/postgres"
86
+
87
+ # With Supabase URL + password
88
+ npx autoship init --supabase-url https://xxx.supabase.co --db-password mypassword
74
89
 
75
90
  # Using environment variables
76
- SUPABASE_URL=https://xxx.supabase.co SUPABASE_SERVICE_KEY=eyJ... npx autoship init
91
+ DATABASE_URL="postgresql://..." npx autoship init
77
92
  `);
78
93
  }
79
94
  function createReadlineInterface() {
@@ -127,142 +142,187 @@ async function promptSecret(rl, question) {
127
142
  stdin.resume();
128
143
  });
129
144
  }
130
- async function getCredentials(options) {
131
- let url = options.supabaseUrl || process.env.SUPABASE_URL || "";
132
- let serviceKey = options.supabaseServiceKey || process.env.SUPABASE_SERVICE_KEY || "";
133
- if (url && serviceKey) {
134
- return { url, serviceKey };
145
+ /**
146
+ * Constructs a DATABASE_URL from a Supabase project URL and password.
147
+ *
148
+ * Supabase URL format: https://[project-ref].supabase.co
149
+ * Database URL format: postgresql://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres
150
+ *
151
+ * Note: We use the transaction pooler (port 6543) for compatibility.
152
+ * The region is detected from the Supabase URL or defaults to us-east-1.
153
+ */
154
+ function constructDatabaseUrl(supabaseUrl, password) {
155
+ const url = new URL(supabaseUrl);
156
+ const hostname = url.hostname; // e.g., "xxx.supabase.co"
157
+ const projectRef = hostname.split(".")[0];
158
+ if (!projectRef) {
159
+ throw new Error("Could not extract project reference from Supabase URL");
160
+ }
161
+ // Supabase uses different regional pooler endpoints
162
+ // Default to us-east-1, but this can be adjusted
163
+ // The actual region is embedded in the project, but we can't easily detect it
164
+ // Users can provide DATABASE_URL directly if they need a specific region
165
+ const region = "us-east-1";
166
+ const encodedPassword = encodeURIComponent(password);
167
+ return `postgresql://postgres.${projectRef}:${encodedPassword}@aws-0-${region}.pooler.supabase.com:6543/postgres`;
168
+ }
169
+ async function getConnectionUrl(options) {
170
+ // Priority: CLI args > env vars > interactive prompt
171
+ // Check for direct DATABASE_URL first
172
+ let databaseUrl = options.databaseUrl || process.env.DATABASE_URL;
173
+ if (databaseUrl) {
174
+ return databaseUrl;
175
+ }
176
+ // Check for SUPABASE_URL + DB_PASSWORD combination
177
+ let supabaseUrl = options.supabaseUrl || process.env.SUPABASE_URL;
178
+ let dbPassword = options.dbPassword || process.env.DB_PASSWORD;
179
+ if (supabaseUrl && dbPassword) {
180
+ return constructDatabaseUrl(supabaseUrl, dbPassword);
135
181
  }
182
+ // Interactive mode
136
183
  const rl = createReadlineInterface();
137
184
  try {
138
185
  console.log("\n Autoship Database Setup\n");
139
186
  console.log(" This will create the required tables in your Supabase database.\n");
140
- if (!url) {
141
- url = await prompt(rl, " Supabase URL: ");
142
- }
143
- else {
144
- console.log(` Supabase URL: ${url}`);
145
- }
146
- if (!serviceKey) {
147
- serviceKey = await promptSecret(rl, " Supabase Service Key: ");
187
+ console.log(" You can provide credentials in two ways:");
188
+ console.log(" 1. Full DATABASE_URL (includes password)");
189
+ console.log(" 2. Supabase URL + database password\n");
190
+ const choice = await prompt(rl, " Enter (1) for DATABASE_URL or (2) for Supabase URL + password: ");
191
+ if (choice === "1") {
192
+ console.log("\n Find your DATABASE_URL in Supabase Dashboard:");
193
+ console.log(" Project Settings > Database > Connection string > URI\n");
194
+ databaseUrl = await promptSecret(rl, " DATABASE_URL: ");
195
+ return databaseUrl;
148
196
  }
149
197
  else {
150
- console.log(" Supabase Service Key: [provided]");
198
+ if (!supabaseUrl) {
199
+ supabaseUrl = await prompt(rl, "\n Supabase URL (e.g., https://xxx.supabase.co): ");
200
+ }
201
+ else {
202
+ console.log(`\n Supabase URL: ${supabaseUrl}`);
203
+ }
204
+ if (!dbPassword) {
205
+ console.log("\n Find your database password in Supabase Dashboard:");
206
+ console.log(" Project Settings > Database > Database password\n");
207
+ dbPassword = await promptSecret(rl, " Database password: ");
208
+ }
209
+ return constructDatabaseUrl(supabaseUrl, dbPassword);
151
210
  }
152
- return { url, serviceKey };
153
211
  }
154
212
  finally {
155
213
  rl.close();
156
214
  }
157
215
  }
158
- function validateCredentials(url, serviceKey) {
159
- if (!url) {
160
- console.error("\n Error: Supabase URL is required");
161
- process.exit(1);
162
- }
163
- if (!serviceKey) {
164
- console.error("\n Error: Supabase Service Key is required");
216
+ function validateDatabaseUrl(databaseUrl) {
217
+ if (!databaseUrl) {
218
+ console.error("\n Error: Database connection URL is required");
165
219
  process.exit(1);
166
220
  }
167
221
  try {
168
- new URL(url);
222
+ const url = new URL(databaseUrl);
223
+ if (!url.protocol.startsWith("postgres")) {
224
+ throw new Error("Not a PostgreSQL URL");
225
+ }
169
226
  }
170
227
  catch {
171
- console.error("\n Error: Invalid Supabase URL format");
172
- process.exit(1);
173
- }
174
- if (!serviceKey.startsWith("eyJ")) {
175
- console.error("\n Error: Service key should be a JWT (starts with 'eyJ')");
176
- console.error(" Make sure you're using the service_role key, not the anon key.");
228
+ console.error("\n Error: Invalid DATABASE_URL format");
229
+ console.error(" Expected format: postgresql://user:password@host:port/database");
177
230
  process.exit(1);
178
231
  }
179
232
  }
180
- async function runMigration(url, serviceKey, migrationSql) {
181
- console.log("\n Connecting to Supabase...");
182
- const supabase = createClient(url, serviceKey, {
183
- auth: {
184
- autoRefreshToken: false,
185
- persistSession: false,
186
- },
233
+ async function runMigration(databaseUrl, migrationSql) {
234
+ console.log("\n Connecting to database...");
235
+ const sql = postgres(databaseUrl, {
236
+ ssl: "require",
237
+ connect_timeout: 10,
187
238
  });
188
- console.log(" Checking database schema...\n");
189
- // First, let's verify the connection by trying to query
190
- const { error: connectionError } = await supabase
191
- .from("agent_tasks")
192
- .select("id")
193
- .limit(1);
194
- if (connectionError && connectionError.code === "42P01") {
195
- // Table doesn't exist - we need to create it
196
- // Since we can't run arbitrary SQL via the REST API, we'll provide instructions
197
- console.log(" Tables don't exist yet.\n");
198
- console.log(" ============================================================");
199
- console.log(" Supabase REST API doesn't support direct SQL execution.");
200
- console.log(" Please run the migration manually using one of these options:");
201
- console.log(" ============================================================\n");
202
- console.log(" Option 1: Supabase Dashboard");
203
- console.log(" 1. Go to your Supabase project dashboard");
204
- console.log(" 2. Navigate to SQL Editor");
205
- console.log(" 3. Copy and paste the contents of: ./autoship-migration.sql\n");
206
- console.log(" Option 2: Supabase CLI");
207
- console.log(" 1. Install Supabase CLI: npm install -g supabase");
208
- console.log(" 2. Link your project: supabase link --project-ref YOUR_PROJECT_REF");
209
- console.log(" 3. Copy autoship-migration.sql to supabase/migrations/");
210
- console.log(" 4. Run: supabase db push\n");
211
- // Write the migration file locally for convenience
212
- const outputPath = path.join(process.cwd(), "autoship-migration.sql");
213
- fs.writeFileSync(outputPath, migrationSql.trim());
214
- console.log(` Migration SQL saved to: ${outputPath}\n`);
215
- console.log(" After running the migration, run this setup command again to verify.\n");
216
- process.exit(0);
217
- }
218
- else if (connectionError) {
219
- console.error(`\n Error connecting to Supabase: ${connectionError.message}`);
220
- process.exit(1);
221
- }
222
- // Tables exist - verify the schema
223
- console.log(" Verifying database schema...\n");
224
- const tables = ["agent_tasks", "task_categories", "task_category_assignments", "task_questions"];
225
- let allTablesExist = true;
226
- for (const table of tables) {
227
- const { error } = await supabase.from(table).select("*").limit(0);
228
- if (error && error.code === "42P01") {
229
- console.log(` [ ] ${table} - NOT FOUND`);
230
- allTablesExist = false;
239
+ try {
240
+ // Test the connection
241
+ await sql `SELECT 1`;
242
+ console.log(" Connected successfully.\n");
243
+ console.log(" Running migrations...\n");
244
+ // Execute the migration SQL
245
+ await sql.unsafe(migrationSql);
246
+ console.log(" Migrations completed successfully!\n");
247
+ // Verify the schema
248
+ console.log(" Verifying database schema...\n");
249
+ const tables = ["agent_tasks", "task_categories", "task_category_assignments", "task_questions"];
250
+ let allTablesExist = true;
251
+ for (const table of tables) {
252
+ try {
253
+ await sql.unsafe(`SELECT 1 FROM ${table} LIMIT 0`);
254
+ console.log(` [x] ${table}`);
255
+ }
256
+ catch (error) {
257
+ console.log(` [ ] ${table} - NOT FOUND`);
258
+ allTablesExist = false;
259
+ }
231
260
  }
232
- else if (error) {
233
- console.log(` [?] ${table} - Error: ${error.message}`);
261
+ if (allTablesExist) {
262
+ console.log("\n All tables created. Your database is ready!\n");
263
+ console.log(" Next steps:");
264
+ console.log(" 1. Add SUPABASE_URL and SUPABASE_ANON_KEY to your app's environment");
265
+ console.log(" 2. Wrap your app with <AutoshipProvider>");
266
+ console.log(" 3. Add the <AutoshipButton /> component\n");
267
+ console.log(" Example:");
268
+ console.log(" import { AutoshipProvider, AutoshipButton } from '@autoship/react';");
269
+ console.log("");
270
+ console.log(" function App() {");
271
+ console.log(" return (");
272
+ console.log(" <AutoshipProvider");
273
+ console.log(" supabaseUrl={process.env.SUPABASE_URL}");
274
+ console.log(" supabaseAnonKey={process.env.SUPABASE_ANON_KEY}");
275
+ console.log(" >");
276
+ console.log(" <YourApp />");
277
+ console.log(" <AutoshipButton />");
278
+ console.log(" </AutoshipProvider>");
279
+ console.log(" );");
280
+ console.log(" }\n");
234
281
  }
235
282
  else {
236
- console.log(` [x] ${table}`);
283
+ console.log("\n Warning: Some tables may not have been created correctly.");
284
+ console.log(" Please check the migration output above for errors.\n");
285
+ process.exit(1);
237
286
  }
238
287
  }
239
- if (allTablesExist) {
240
- console.log("\n All tables exist. Your database is ready!\n");
241
- console.log(" Next steps:");
242
- console.log(" 1. Add SUPABASE_URL and SUPABASE_ANON_KEY to your app's environment");
243
- console.log(" 2. Wrap your app with <AutoshipProvider>");
244
- console.log(" 3. Add the <AutoshipButton /> component\n");
245
- console.log(" Example:");
246
- console.log(" import { AutoshipProvider, AutoshipButton } from '@autoship/react';");
247
- console.log("");
248
- console.log(" function App() {");
249
- console.log(" return (");
250
- console.log(" <AutoshipProvider");
251
- console.log(" supabaseUrl={process.env.SUPABASE_URL}");
252
- console.log(" supabaseAnonKey={process.env.SUPABASE_ANON_KEY}");
253
- console.log(" >");
254
- console.log(" <YourApp />");
255
- console.log(" <AutoshipButton />");
256
- console.log(" </AutoshipProvider>");
257
- console.log(" );");
258
- console.log(" }\n");
288
+ catch (error) {
289
+ const err = error;
290
+ if (err.code === "28P01") {
291
+ console.error("\n Error: Authentication failed. Check your database password.");
292
+ }
293
+ else if (err.code === "ENOTFOUND" || err.code === "ECONNREFUSED") {
294
+ console.error("\n Error: Could not connect to database. Check your connection URL.");
295
+ }
296
+ else if (err.message?.includes("already exists")) {
297
+ console.log("\n Schema already exists. Verifying tables...\n");
298
+ // Verify existing schema
299
+ const tables = ["agent_tasks", "task_categories", "task_category_assignments", "task_questions"];
300
+ let allTablesExist = true;
301
+ for (const table of tables) {
302
+ try {
303
+ await sql.unsafe(`SELECT 1 FROM ${table} LIMIT 0`);
304
+ console.log(` [x] ${table}`);
305
+ }
306
+ catch {
307
+ console.log(` [ ] ${table} - NOT FOUND`);
308
+ allTablesExist = false;
309
+ }
310
+ }
311
+ if (allTablesExist) {
312
+ console.log("\n All tables exist. Your database is ready!\n");
313
+ }
314
+ else {
315
+ console.log("\n Some tables are missing. You may need to run migrations manually.\n");
316
+ process.exit(1);
317
+ }
318
+ }
319
+ else {
320
+ console.error(`\n Error: ${err.message}`);
321
+ process.exit(1);
322
+ }
259
323
  }
260
- else {
261
- console.log("\n Some tables are missing. Please run the migration SQL.");
262
- const outputPath = path.join(process.cwd(), "autoship-migration.sql");
263
- fs.writeFileSync(outputPath, migrationSql.trim());
264
- console.log(` Migration SQL saved to: ${outputPath}\n`);
265
- process.exit(1);
324
+ finally {
325
+ await sql.end();
266
326
  }
267
327
  }
268
328
  export async function run(args) {
@@ -270,9 +330,9 @@ export async function run(args) {
270
330
  try {
271
331
  // Load migrations from the bundled files
272
332
  const migrationSql = loadMigrations();
273
- const { url, serviceKey } = await getCredentials(options);
274
- validateCredentials(url, serviceKey);
275
- await runMigration(url, serviceKey, migrationSql);
333
+ const databaseUrl = await getConnectionUrl(options);
334
+ validateDatabaseUrl(databaseUrl);
335
+ await runMigration(databaseUrl, migrationSql);
276
336
  }
277
337
  catch (error) {
278
338
  console.error("\n Unexpected error:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autoship/react",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -23,7 +23,8 @@
23
23
  "cli": "node dist/cli/autoship.js"
24
24
  },
25
25
  "dependencies": {
26
- "@supabase/supabase-js": "^2.90.1"
26
+ "@supabase/supabase-js": "^2.90.1",
27
+ "postgres": "^3.4.5"
27
28
  },
28
29
  "peerDependencies": {
29
30
  "react": "^18.0.0 || ^19.0.0"