ff-serv 0.1.4 → 0.1.6

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.
@@ -0,0 +1,46 @@
1
+ import * as platform from '@effect/platform';
2
+ import { Effect } from 'effect';
3
+
4
+ export interface DatabaseSource {
5
+ readonly getConnectionUrl: Effect.Effect<
6
+ string,
7
+ Error,
8
+ platform.CommandExecutor.CommandExecutor
9
+ >;
10
+ readonly displayName: string;
11
+ }
12
+
13
+ export const createRailwaySource = (config: {
14
+ projectId: string;
15
+ environmentId: string;
16
+ serviceId: string;
17
+ }): DatabaseSource => ({
18
+ displayName: 'Railway',
19
+ getConnectionUrl: Effect.gen(function* () {
20
+ yield* platform.Command.make(
21
+ 'railway',
22
+ 'link',
23
+ `--project=${config.projectId}`,
24
+ `--environment=${config.environmentId}`,
25
+ `--service=${config.serviceId}`,
26
+ ).pipe(
27
+ platform.Command.stdout('inherit'),
28
+ platform.Command.exitCode,
29
+ );
30
+
31
+ const output = yield* platform.Command.make(
32
+ 'railway',
33
+ 'run',
34
+ 'node',
35
+ '-e',
36
+ 'console.log(process.env.DATABASE_PUBLIC_URL)',
37
+ ).pipe(platform.Command.string);
38
+
39
+ return output.trim();
40
+ }),
41
+ });
42
+
43
+ export const createDirectSource = (url: string): DatabaseSource => ({
44
+ displayName: 'Direct Connection',
45
+ getConnectionUrl: Effect.succeed(url),
46
+ });
@@ -0,0 +1,87 @@
1
+ import { Effect } from 'effect';
2
+ import inquirer from 'inquirer';
3
+
4
+ const typedPrompt = <T>(questions: Parameters<typeof inquirer.prompt>[0]) =>
5
+ Effect.tryPromise(() => inquirer.prompt(questions) as Promise<T>);
6
+
7
+ export const promptDatabaseSourceType = typedPrompt<{
8
+ sourceType: 'railway' | 'direct';
9
+ }>([
10
+ {
11
+ type: 'list',
12
+ name: 'sourceType',
13
+ message: 'Select database source type:',
14
+ choices: [
15
+ { name: 'Railway', value: 'railway' },
16
+ { name: 'Direct URL', value: 'direct' },
17
+ ],
18
+ },
19
+ ]).pipe(Effect.map((r) => r.sourceType));
20
+
21
+ export const promptRailwayConfig = typedPrompt<{
22
+ projectId: string;
23
+ environmentId: string;
24
+ serviceId: string;
25
+ }>([
26
+ {
27
+ type: 'input',
28
+ // @ts-expect-error - expected, need better typing. Try callling it directly with inquirer.prompt, it's overload issue
29
+ name: 'projectId',
30
+ message: 'Enter Railway Project ID:',
31
+ validate: (input: string) =>
32
+ input.trim().length > 0 || 'Project ID is required',
33
+ },
34
+ {
35
+ type: 'input',
36
+ // @ts-expect-error - expected, need better typing. Try callling it directly with inquirer.prompt, it's overload issue
37
+ name: 'environmentId',
38
+ message: 'Enter Railway Environment ID:',
39
+ validate: (input: string) =>
40
+ input.trim().length > 0 || 'Environment ID is required',
41
+ },
42
+ {
43
+ type: 'input',
44
+ // @ts-expect-error - expected, need better typing. Try callling it directly with inquirer.prompt, it's overload issue
45
+ name: 'serviceId',
46
+ message: 'Enter Railway Service ID:',
47
+ validate: (input: string) =>
48
+ input.trim().length > 0 || 'Service ID is required',
49
+ },
50
+ ]);
51
+
52
+ export const promptDirectUrl = typedPrompt<{ databaseUrl: string }>([
53
+ {
54
+ type: 'input',
55
+ name: 'databaseUrl',
56
+ message: 'Enter source database URL:',
57
+ },
58
+ ]).pipe(Effect.map((r) => r.databaseUrl));
59
+
60
+ export const promptTargetUrl = (defaultUrl?: string) =>
61
+ typedPrompt<{ targetUrl: string }>([
62
+ {
63
+ type: 'input',
64
+ name: 'targetUrl',
65
+ message: 'Enter target database URL:',
66
+ default: defaultUrl,
67
+ },
68
+ ]).pipe(Effect.map((r) => r.targetUrl));
69
+
70
+ export const promptRetry = typedPrompt<{ retry: boolean }>([
71
+ {
72
+ type: 'confirm',
73
+ name: 'retry',
74
+ message: 'An error occurred. Would you like to retry?',
75
+ default: true,
76
+ },
77
+ ]).pipe(Effect.map((r) => r.retry));
78
+
79
+ export const promptCleanupDump = (dumpPath: string) =>
80
+ typedPrompt<{ cleanup: boolean }>([
81
+ {
82
+ type: 'confirm',
83
+ name: 'cleanup',
84
+ message: `Delete dump file at ${dumpPath}?`,
85
+ default: false,
86
+ },
87
+ ]).pipe(Effect.map((r) => r.cleanup));