instant-cli 0.22.177 → 0.22.178
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/.turbo/turbo-build.log +2 -2
- package/__tests__/e2e/cli.e2e.test.ts +3 -3
- package/__tests__/e2e/helpers.ts +1 -1
- package/__tests__/effectHelpers.ts +45 -0
- package/__tests__/mergeSchema.test.ts +2 -2
- package/dist/commands/claim.d.ts +6 -0
- package/dist/commands/claim.d.ts.map +1 -0
- package/dist/commands/claim.js +22 -0
- package/dist/commands/claim.js.map +1 -0
- package/dist/commands/explorer.d.ts +6 -0
- package/dist/commands/explorer.d.ts.map +1 -0
- package/dist/commands/explorer.js +13 -0
- package/dist/commands/explorer.js.map +1 -0
- package/dist/commands/info.d.ts +3 -0
- package/dist/commands/info.d.ts.map +1 -0
- package/dist/commands/info.js +24 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +39 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/initWithoutFiles.d.ts +6 -0
- package/dist/commands/initWithoutFiles.d.ts.map +1 -0
- package/dist/commands/initWithoutFiles.js +64 -0
- package/dist/commands/initWithoutFiles.js.map +1 -0
- package/dist/commands/login.d.ts +9 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +52 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +4 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +21 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/pull.d.ts +6 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +16 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +6 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +20 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/query.d.ts +7 -0
- package/dist/commands/query.d.ts.map +1 -0
- package/dist/commands/query.js +52 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/context/authToken.d.ts +30 -0
- package/dist/context/authToken.d.ts.map +1 -0
- package/dist/context/authToken.js +86 -0
- package/dist/context/authToken.js.map +1 -0
- package/dist/context/currentApp.d.ts +37 -0
- package/dist/context/currentApp.d.ts.map +1 -0
- package/dist/context/currentApp.js +204 -0
- package/dist/context/currentApp.js.map +1 -0
- package/dist/context/globalOpts.d.ts +11 -0
- package/dist/context/globalOpts.d.ts.map +1 -0
- package/dist/context/globalOpts.js +13 -0
- package/dist/context/globalOpts.js.map +1 -0
- package/dist/context/platformApi.d.ts +19 -0
- package/dist/context/platformApi.d.ts.map +1 -0
- package/dist/context/platformApi.js +24 -0
- package/dist/context/platformApi.js.map +1 -0
- package/dist/context/projectInfo.d.ts +29 -0
- package/dist/context/projectInfo.d.ts.map +1 -0
- package/dist/context/projectInfo.js +149 -0
- package/dist/context/projectInfo.js.map +1 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +6 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +41 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +169 -1781
- package/dist/index.js.map +1 -1
- package/dist/layer.d.ts +23 -0
- package/dist/layer.d.ts.map +1 -0
- package/dist/layer.js +68 -0
- package/dist/layer.js.map +1 -0
- package/dist/lib/createApp.d.ts +12 -0
- package/dist/lib/createApp.d.ts.map +1 -0
- package/dist/lib/createApp.js +15 -0
- package/dist/lib/createApp.js.map +1 -0
- package/dist/lib/handleEnv.d.ts +7 -0
- package/dist/lib/handleEnv.d.ts.map +1 -0
- package/dist/lib/handleEnv.js +91 -0
- package/dist/lib/handleEnv.js.map +1 -0
- package/dist/lib/http.d.ts +32 -0
- package/dist/lib/http.d.ts.map +1 -0
- package/dist/lib/http.js +67 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/login.d.ts +13 -0
- package/dist/lib/login.d.ts.map +1 -0
- package/dist/lib/login.js +39 -0
- package/dist/lib/login.js.map +1 -0
- package/dist/lib/pullPerms.d.ts +7 -0
- package/dist/lib/pullPerms.d.ts.map +1 -0
- package/dist/lib/pullPerms.js +41 -0
- package/dist/lib/pullPerms.js.map +1 -0
- package/dist/lib/pullSchema.d.ts +12 -0
- package/dist/lib/pullSchema.d.ts.map +1 -0
- package/dist/lib/pullSchema.js +62 -0
- package/dist/lib/pullSchema.js.map +1 -0
- package/dist/lib/pushPerms.d.ts +13 -0
- package/dist/lib/pushPerms.d.ts.map +1 -0
- package/dist/lib/pushPerms.js +54 -0
- package/dist/lib/pushPerms.js.map +1 -0
- package/dist/lib/pushSchema.d.ts +53 -0
- package/dist/lib/pushSchema.d.ts.map +1 -0
- package/dist/lib/pushSchema.js +160 -0
- package/dist/lib/pushSchema.js.map +1 -0
- package/dist/lib/ui.d.ts +16 -0
- package/dist/lib/ui.d.ts.map +1 -0
- package/dist/lib/ui.js +22 -0
- package/dist/lib/ui.js.map +1 -0
- package/dist/logging.d.ts +4 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +17 -0
- package/dist/logging.js.map +1 -0
- package/dist/old.d.ts +14 -0
- package/dist/old.d.ts.map +1 -0
- package/dist/old.js +417 -0
- package/dist/old.js.map +1 -0
- package/dist/program.d.ts +3 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +3 -0
- package/dist/program.js.map +1 -0
- package/dist/renderSchemaPlan.d.ts +3 -3
- package/dist/renderSchemaPlan.d.ts.map +1 -1
- package/dist/renderSchemaPlan.js +2 -14
- package/dist/renderSchemaPlan.js.map +1 -1
- package/dist/ui/index.d.ts +4 -3
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +2 -2
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/lib.js +1 -0
- package/dist/ui/lib.js.map +1 -1
- package/dist/util/findConfigCandidates.d.ts +1 -1
- package/dist/util/findConfigCandidates.d.ts.map +1 -1
- package/dist/util/findConfigCandidates.js +1 -3
- package/dist/util/findConfigCandidates.js.map +1 -1
- package/dist/util/fs.d.ts +1 -1
- package/dist/util/fs.d.ts.map +1 -1
- package/dist/util/fs.js.map +1 -1
- package/dist/util/getAuthPaths.d.ts.map +1 -1
- package/dist/util/getAuthPaths.js.map +1 -1
- package/dist/util/isHeadlessEnvironment.d.ts +3 -1
- package/dist/util/isHeadlessEnvironment.d.ts.map +1 -1
- package/dist/util/isHeadlessEnvironment.js.map +1 -1
- package/dist/util/loadConfig.d.ts +1 -1
- package/dist/util/loadConfig.d.ts.map +1 -1
- package/dist/util/loadConfig.js +2 -2
- package/dist/util/loadConfig.js.map +1 -1
- package/dist/util/mergeSchema.d.ts +9 -1
- package/dist/util/mergeSchema.d.ts.map +1 -1
- package/dist/util/mergeSchema.js +4 -0
- package/dist/util/mergeSchema.js.map +1 -1
- package/dist/util/renamePrompt.d.ts +2 -1
- package/dist/util/renamePrompt.d.ts.map +1 -1
- package/dist/util/renamePrompt.js +1 -1
- package/dist/util/renamePrompt.js.map +1 -1
- package/package.json +17 -7
- package/src/commands/claim.ts +31 -0
- package/src/commands/explorer.ts +21 -0
- package/src/commands/info.ts +34 -0
- package/src/commands/init.ts +58 -0
- package/src/commands/initWithoutFiles.ts +107 -0
- package/src/commands/login.ts +76 -0
- package/src/commands/logout.ts +23 -0
- package/src/commands/pull.ts +23 -0
- package/src/commands/push.ts +25 -0
- package/src/commands/query.ts +61 -0
- package/src/context/authToken.ts +149 -0
- package/src/context/currentApp.ts +277 -0
- package/src/context/globalOpts.ts +22 -0
- package/src/context/platformApi.ts +35 -0
- package/src/context/projectInfo.ts +215 -0
- package/src/errors.ts +7 -0
- package/src/index.ts +428 -0
- package/src/layer.ts +155 -0
- package/src/lib/createApp.ts +28 -0
- package/src/lib/handleEnv.ts +115 -0
- package/src/lib/http.ts +148 -0
- package/src/lib/login.ts +54 -0
- package/src/lib/pullPerms.ts +50 -0
- package/src/lib/pullSchema.ts +95 -0
- package/src/lib/pushPerms.ts +80 -0
- package/src/lib/pushSchema.ts +240 -0
- package/src/lib/ui.ts +36 -0
- package/src/logging.ts +32 -0
- package/src/old.js +495 -0
- package/src/program.ts +3 -0
- package/src/renderSchemaPlan.ts +6 -18
- package/src/ui/index.ts +4 -3
- package/src/util/findConfigCandidates.ts +1 -2
- package/src/util/fs.ts +1 -1
- package/src/util/getAuthPaths.ts +1 -0
- package/src/util/isHeadlessEnvironment.ts +1 -1
- package/src/util/loadConfig.ts +3 -6
- package/src/util/{mergeSchema.js → mergeSchema.ts} +26 -16
- package/src/util/renamePrompt.ts +2 -1
- package/tsconfig.build.json +20 -0
- package/tsconfig.json +15 -5
- package/vitest.config.ts +2 -1
- package/dist/util/packageManager.d.ts +0 -3
- package/dist/util/packageManager.d.ts.map +0 -1
- package/dist/util/packageManager.js +0 -70
- package/dist/util/packageManager.js.map +0 -1
- package/dist/util/promptOk.d.ts +0 -4
- package/dist/util/promptOk.d.ts.map +0 -1
- package/dist/util/promptOk.js +0 -18
- package/dist/util/promptOk.js.map +0 -1
- package/src/index.js +0 -2333
- package/src/util/packageManager.js +0 -78
- package/src/util/promptOk.ts +0 -26
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import { potentialEnvs } from '../context/currentApp.ts';
|
|
3
|
+
import type { CurrentAppInfo } from '../context/currentApp.ts';
|
|
4
|
+
import { ProjectInfo, ProjectInfoError } from '../context/projectInfo.ts';
|
|
5
|
+
import { readPackage } from 'pkg-types';
|
|
6
|
+
import { GlobalOpts } from '../context/globalOpts.ts';
|
|
7
|
+
import { FileSystem, Path } from '@effect/platform';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import terminalLink from 'terminal-link';
|
|
10
|
+
import { getDashUrl } from './http.ts';
|
|
11
|
+
import { promptOk } from './ui.ts';
|
|
12
|
+
|
|
13
|
+
export const handleEnv = Effect.fn(function* (app: CurrentAppInfo) {
|
|
14
|
+
const opts = yield* GlobalOpts;
|
|
15
|
+
const { pkgDir } = yield* ProjectInfo;
|
|
16
|
+
const envType = yield* detectEnvType;
|
|
17
|
+
const envName = potentialEnvs[envType];
|
|
18
|
+
const envFile = opts.env ?? '.env';
|
|
19
|
+
const fs = yield* FileSystem.FileSystem;
|
|
20
|
+
const path = yield* Path.Path;
|
|
21
|
+
const hasEnvFile = yield* fs.exists(path.join(pkgDir, envFile));
|
|
22
|
+
const dashOrigin = yield* getDashUrl;
|
|
23
|
+
if (hasEnvFile) {
|
|
24
|
+
return printDotEnvInfo(envType, app.appId, dashOrigin);
|
|
25
|
+
}
|
|
26
|
+
yield* Effect.log(
|
|
27
|
+
`\nLooks like you don't have a ${chalk.green(`\`${envFile}\``)} file yet.`,
|
|
28
|
+
);
|
|
29
|
+
yield* Effect.log(
|
|
30
|
+
`If we set ${chalk.green(envName)} & ${chalk.green('INSTANT_APP_ADMIN_TOKEN')}, we can remember the app that you chose for all future commands.`,
|
|
31
|
+
);
|
|
32
|
+
const saveExtraInfo =
|
|
33
|
+
envFile !== '.env' ? chalk.green(' (will create `' + envFile + '`)') : '';
|
|
34
|
+
|
|
35
|
+
const ok = yield* promptOk(
|
|
36
|
+
{
|
|
37
|
+
inline: true,
|
|
38
|
+
promptText: 'Want us to create this env file for you?' + saveExtraInfo,
|
|
39
|
+
modifyOutput: (a) => a,
|
|
40
|
+
},
|
|
41
|
+
true,
|
|
42
|
+
);
|
|
43
|
+
if (!ok) {
|
|
44
|
+
yield* Effect.log(
|
|
45
|
+
`No .env file created. You can always set ${chalk.green('`' + envName + '`')} later. \n`,
|
|
46
|
+
);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const content =
|
|
50
|
+
[
|
|
51
|
+
[envName, app.appId],
|
|
52
|
+
['INSTANT_APP_ADMIN_TOKEN', app.adminToken],
|
|
53
|
+
]
|
|
54
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
55
|
+
.join('\n') + '\n';
|
|
56
|
+
|
|
57
|
+
yield* fs.writeFileString(path.join(pkgDir, envFile), content);
|
|
58
|
+
|
|
59
|
+
if (envFile !== '.env') {
|
|
60
|
+
yield* Effect.log(`Created ${chalk.green(envFile)}!`);
|
|
61
|
+
} else {
|
|
62
|
+
yield* Effect.log(`Created ${chalk.green('.env')} file!`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const detectEnvType = Effect.gen(function* () {
|
|
67
|
+
const pkgJson = yield* Effect.tryPromise({
|
|
68
|
+
try: () => readPackage(),
|
|
69
|
+
catch: () =>
|
|
70
|
+
new ProjectInfoError({ message: "Couldn't read package.json" }),
|
|
71
|
+
});
|
|
72
|
+
if (pkgJson.dependencies?.next) {
|
|
73
|
+
return 'next';
|
|
74
|
+
}
|
|
75
|
+
if (pkgJson.devDependencies?.svelte) {
|
|
76
|
+
return 'svelte';
|
|
77
|
+
}
|
|
78
|
+
if (pkgJson.devDependencies?.vite) {
|
|
79
|
+
return 'vite';
|
|
80
|
+
}
|
|
81
|
+
if (pkgJson.dependencies?.expo) {
|
|
82
|
+
return 'expo';
|
|
83
|
+
}
|
|
84
|
+
if (pkgJson.dependencies?.nuxt) {
|
|
85
|
+
return 'nuxt';
|
|
86
|
+
}
|
|
87
|
+
if (pkgJson.dependencies?.['@types/bun']) {
|
|
88
|
+
return 'bun';
|
|
89
|
+
}
|
|
90
|
+
return 'catchall';
|
|
91
|
+
}).pipe(Effect.catchTag('ProjectInfoError', () => Effect.succeed('catchall')));
|
|
92
|
+
|
|
93
|
+
function printDotEnvInfo(
|
|
94
|
+
envType: keyof typeof potentialEnvs,
|
|
95
|
+
appId: string,
|
|
96
|
+
dashOrigin: string,
|
|
97
|
+
) {
|
|
98
|
+
console.log(`\nPicked app ${chalk.green(appId)}!\n`);
|
|
99
|
+
console.log(
|
|
100
|
+
`To use this app automatically from now on, update your ${chalk.green('`.env`')} file:`,
|
|
101
|
+
);
|
|
102
|
+
const picked = potentialEnvs[envType];
|
|
103
|
+
const rest = { ...potentialEnvs };
|
|
104
|
+
delete rest[envType];
|
|
105
|
+
console.log(` ${chalk.green(picked)}=${appId}`);
|
|
106
|
+
const otherEnvs = Object.values(rest);
|
|
107
|
+
otherEnvs.sort();
|
|
108
|
+
const otherEnvStr = otherEnvs.map((x) => ' ' + chalk.green(x)).join('\n');
|
|
109
|
+
console.log(`Alternative names: \n${otherEnvStr} \n`);
|
|
110
|
+
console.log(terminalLink('Dashboard:', appDashUrl(appId, dashOrigin)) + '\n');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function appDashUrl(id: string, instantOrigin: string) {
|
|
114
|
+
return `${instantOrigin}/dash?s=main&t=home&app=${id}`;
|
|
115
|
+
}
|
package/src/lib/http.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { HttpClient, HttpClientRequest } from '@effect/platform';
|
|
2
|
+
import { version } from '@instantdb/version';
|
|
3
|
+
import { Config, Context, Data, Effect, Layer, Option, Schema } from 'effect';
|
|
4
|
+
import { AuthToken } from '../context/authToken.ts';
|
|
5
|
+
import { TimeoutException } from 'effect/Cause';
|
|
6
|
+
import { RequestError } from '@effect/platform/HttpClientError';
|
|
7
|
+
|
|
8
|
+
export class InstantHttp extends Context.Tag(
|
|
9
|
+
'instant-cli/new/lib/http/InstantHttp',
|
|
10
|
+
)<
|
|
11
|
+
InstantHttp,
|
|
12
|
+
HttpClient.HttpClient.With<InstantHttpError | TimeoutException | RequestError>
|
|
13
|
+
>() {}
|
|
14
|
+
|
|
15
|
+
export class InstantHttpAuthed extends Context.Tag(
|
|
16
|
+
'instant-cli/new/lib/http/InstantHttpAuthed',
|
|
17
|
+
)<
|
|
18
|
+
InstantHttpAuthed,
|
|
19
|
+
HttpClient.HttpClient.With<InstantHttpError | TimeoutException | RequestError>
|
|
20
|
+
>() {}
|
|
21
|
+
|
|
22
|
+
export class InstantHttpError extends Data.TaggedError('InstantHttpError')<
|
|
23
|
+
{
|
|
24
|
+
methodAndUrl: string;
|
|
25
|
+
} & typeof InstantTypicalHttpErrorResponse.Type
|
|
26
|
+
> {}
|
|
27
|
+
|
|
28
|
+
// Pipe on an http client to set the command header
|
|
29
|
+
export const withCommand = (command: string) => {
|
|
30
|
+
return (
|
|
31
|
+
client: HttpClient.HttpClient.With<
|
|
32
|
+
InstantHttpError | TimeoutException | RequestError
|
|
33
|
+
>,
|
|
34
|
+
) =>
|
|
35
|
+
client.pipe(
|
|
36
|
+
HttpClient.mapRequest((r) =>
|
|
37
|
+
r.pipe(HttpClientRequest.setHeader(`X-Instant-Command`, command)),
|
|
38
|
+
),
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
class InstantTypicalHttpErrorResponse extends Schema.Struct({
|
|
43
|
+
message: Schema.String,
|
|
44
|
+
type: Schema.String.pipe(Schema.optional),
|
|
45
|
+
hint: Schema.Record({ key: Schema.String, value: Schema.Any }).pipe(
|
|
46
|
+
Schema.optional,
|
|
47
|
+
),
|
|
48
|
+
}) {}
|
|
49
|
+
|
|
50
|
+
export const InstantHttpLive = Layer.effect(
|
|
51
|
+
InstantHttp,
|
|
52
|
+
Effect.gen(function* () {
|
|
53
|
+
const client = yield* HttpClient.HttpClient;
|
|
54
|
+
const baseUrl = yield* getBaseUrl;
|
|
55
|
+
return client.pipe(
|
|
56
|
+
HttpClient.mapRequest((r) =>
|
|
57
|
+
r.pipe(
|
|
58
|
+
HttpClientRequest.prependUrl(baseUrl),
|
|
59
|
+
HttpClientRequest.setHeader('X-Instant-Source', 'instant-cli'),
|
|
60
|
+
HttpClientRequest.setHeader('X-Instant-Version', version),
|
|
61
|
+
),
|
|
62
|
+
),
|
|
63
|
+
HttpClient.transformResponse((r) => r.pipe(Effect.timeout('5 minutes'))),
|
|
64
|
+
HttpClient.filterStatusOk, // makes non 2xx http codes error
|
|
65
|
+
// parses the non 2xx errors into known instant error shape
|
|
66
|
+
HttpClient.transformResponse((r) =>
|
|
67
|
+
r.pipe(
|
|
68
|
+
Effect.catchTag('ResponseError', (requestError) =>
|
|
69
|
+
Effect.gen(function* () {
|
|
70
|
+
const jsonBody = yield* requestError.response.json.pipe(
|
|
71
|
+
Effect.andThen(
|
|
72
|
+
Schema.decodeUnknown(InstantTypicalHttpErrorResponse),
|
|
73
|
+
),
|
|
74
|
+
Effect.mapError(
|
|
75
|
+
(e) =>
|
|
76
|
+
new InstantHttpError({
|
|
77
|
+
message:
|
|
78
|
+
'Error making request to ' + requestError.methodAndUrl,
|
|
79
|
+
type: e._tag,
|
|
80
|
+
methodAndUrl: requestError.methodAndUrl,
|
|
81
|
+
}),
|
|
82
|
+
),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// The hint.message is usually better error message
|
|
86
|
+
let message = jsonBody.message;
|
|
87
|
+
if (
|
|
88
|
+
jsonBody.hint?.message &&
|
|
89
|
+
typeof jsonBody.hint.message === 'string'
|
|
90
|
+
) {
|
|
91
|
+
message = jsonBody.hint.message;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return yield* new InstantHttpError({
|
|
95
|
+
message: message,
|
|
96
|
+
methodAndUrl: requestError.methodAndUrl,
|
|
97
|
+
hint: jsonBody.hint,
|
|
98
|
+
type: jsonBody.type || 'Unknown type',
|
|
99
|
+
});
|
|
100
|
+
}),
|
|
101
|
+
),
|
|
102
|
+
),
|
|
103
|
+
),
|
|
104
|
+
);
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
export const InstantHttpAuthedLive = Layer.effect(
|
|
109
|
+
InstantHttpAuthed,
|
|
110
|
+
Effect.gen(function* () {
|
|
111
|
+
const http = yield* InstantHttp;
|
|
112
|
+
const authToken = yield* AuthToken;
|
|
113
|
+
return http.pipe(
|
|
114
|
+
HttpClient.mapRequestEffect((r) =>
|
|
115
|
+
authToken.getAuthToken.pipe(
|
|
116
|
+
Effect.map((token) =>
|
|
117
|
+
r.pipe(
|
|
118
|
+
HttpClientRequest.setHeader('Authorization', `Bearer ${token}`),
|
|
119
|
+
),
|
|
120
|
+
),
|
|
121
|
+
),
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
}),
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
export const getBaseUrl = Effect.gen(function* () {
|
|
128
|
+
const setEnv = yield* Config.string('INSTANT_CLI_API_URI').pipe(
|
|
129
|
+
Config.option,
|
|
130
|
+
);
|
|
131
|
+
const dev = yield* Config.boolean('INSTANT_CLI_DEV').pipe(
|
|
132
|
+
Config.withDefault(false),
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
return Option.match(setEnv, {
|
|
136
|
+
onSome: (url) => url,
|
|
137
|
+
onNone: () => {
|
|
138
|
+
return dev ? 'http://localhost:8888' : 'https://api.instantdb.com';
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
export const getDashUrl = Effect.gen(function* () {
|
|
144
|
+
const dev = Option.getOrNull(
|
|
145
|
+
yield* Config.boolean('INSTANT_CLI_DEV').pipe(Config.option),
|
|
146
|
+
);
|
|
147
|
+
return dev ? 'http://localhost:3000' : 'https://instantdb.com';
|
|
148
|
+
});
|
package/src/lib/login.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Effect, Schedule, Schema } from 'effect';
|
|
2
|
+
import { InstantHttp, withCommand } from './http.ts';
|
|
3
|
+
import {
|
|
4
|
+
HttpClientRequest,
|
|
5
|
+
HttpClientResponse,
|
|
6
|
+
FileSystem,
|
|
7
|
+
} from '@effect/platform';
|
|
8
|
+
import { getAuthPaths } from '../util/getAuthPaths.ts';
|
|
9
|
+
|
|
10
|
+
const LoginInfo = Schema.Struct({
|
|
11
|
+
secret: Schema.String,
|
|
12
|
+
ticket: Schema.String,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const TokenResult = Schema.Struct({
|
|
16
|
+
token: Schema.String,
|
|
17
|
+
email: Schema.String,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const getLoginTicketAndSecret = Effect.gen(function* () {
|
|
21
|
+
const http = yield* InstantHttp;
|
|
22
|
+
const res = yield* http
|
|
23
|
+
.pipe(withCommand('login'))
|
|
24
|
+
.post('/dash/cli/auth/register')
|
|
25
|
+
.pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(LoginInfo)));
|
|
26
|
+
return res;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const waitForAuthToken = Effect.fn(function* (secret: string) {
|
|
30
|
+
const http = (yield* InstantHttp).pipe(withCommand('login'));
|
|
31
|
+
const res = yield* HttpClientRequest.post('/dash/cli/auth/check').pipe(
|
|
32
|
+
HttpClientRequest.bodyUnsafeJson({
|
|
33
|
+
secret,
|
|
34
|
+
}),
|
|
35
|
+
http.execute,
|
|
36
|
+
Effect.flatMap(HttpClientResponse.schemaBodyJson(TokenResult)),
|
|
37
|
+
Effect.retry({
|
|
38
|
+
while: (e) =>
|
|
39
|
+
e._tag === 'InstantHttpError' &&
|
|
40
|
+
e.hint?.errors?.at(0)?.issue === 'waiting-for-user',
|
|
41
|
+
schedule: Schedule.fixed('1 seconds'),
|
|
42
|
+
times: 120,
|
|
43
|
+
}),
|
|
44
|
+
);
|
|
45
|
+
return res;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const saveConfigAuthToken = Effect.fn(function* (token: string) {
|
|
49
|
+
const authPaths = getAuthPaths();
|
|
50
|
+
|
|
51
|
+
const fs = yield* FileSystem.FileSystem;
|
|
52
|
+
yield* fs.makeDirectory(authPaths.appConfigDirPath, { recursive: true });
|
|
53
|
+
yield* fs.writeFileString(authPaths.authConfigFilePath, token);
|
|
54
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Effect, Option, Schema } from 'effect';
|
|
2
|
+
import { CurrentApp } from '../context/currentApp.ts';
|
|
3
|
+
import { InstantHttpAuthed, withCommand } from './http.ts';
|
|
4
|
+
import { HttpClientResponse } from '@effect/platform';
|
|
5
|
+
import { readLocalPermsFile } from '../old.js';
|
|
6
|
+
import { getPermsPathToWrite } from '../util/findConfigCandidates.ts';
|
|
7
|
+
import { promptOk } from './ui.ts';
|
|
8
|
+
import { UI } from '../ui/index.ts';
|
|
9
|
+
import { writeTypescript } from './pullSchema.ts';
|
|
10
|
+
import { generatePermsTypescriptFile } from '@instantdb/platform';
|
|
11
|
+
import { ProjectInfo } from '../context/projectInfo.ts';
|
|
12
|
+
import { Path } from '@effect/platform';
|
|
13
|
+
|
|
14
|
+
export const pullPerms = Effect.gen(function* () {
|
|
15
|
+
yield* Effect.log('\nPulling perms...');
|
|
16
|
+
const { appId } = yield* CurrentApp;
|
|
17
|
+
const http = yield* InstantHttpAuthed;
|
|
18
|
+
const permsResponse = yield* http
|
|
19
|
+
.pipe(withCommand('pull'))
|
|
20
|
+
.get(`/dash/apps/${appId}/perms/pull`)
|
|
21
|
+
.pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(Schema.Any))); // parse result body into "any"
|
|
22
|
+
|
|
23
|
+
const prevPermsFile = yield* Effect.tryPromise(readLocalPermsFile).pipe(
|
|
24
|
+
Effect.map(Option.fromNullable),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const shortPermsPath = Option.match(prevPermsFile, {
|
|
28
|
+
onSome: (file) => file.path,
|
|
29
|
+
onNone: () => getPermsPathToWrite(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (Option.isSome(prevPermsFile)) {
|
|
33
|
+
const shouldContinue = yield* promptOk({
|
|
34
|
+
promptText: `This will overwrite your local ${shortPermsPath} file, OK to proceed?`,
|
|
35
|
+
modifyOutput: UI.modifiers.yPadding,
|
|
36
|
+
inline: true,
|
|
37
|
+
});
|
|
38
|
+
if (!shouldContinue) return;
|
|
39
|
+
}
|
|
40
|
+
const { instantModuleName, pkgDir } = yield* ProjectInfo;
|
|
41
|
+
const fileContent = generatePermsTypescriptFile(
|
|
42
|
+
permsResponse?.perms || {},
|
|
43
|
+
instantModuleName,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const path = yield* Path.Path;
|
|
47
|
+
|
|
48
|
+
yield* writeTypescript(path.join(pkgDir, shortPermsPath), fileContent);
|
|
49
|
+
yield* Effect.log('✅ Wrote permissions to ' + shortPermsPath);
|
|
50
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { FileSystem, HttpClientResponse, Path } from '@effect/platform';
|
|
2
|
+
import {
|
|
3
|
+
apiSchemaToInstantSchemaDef,
|
|
4
|
+
generateSchemaTypescriptFile,
|
|
5
|
+
} from '@instantdb/platform';
|
|
6
|
+
import { Effect, Schema } from 'effect';
|
|
7
|
+
import prettier from 'prettier';
|
|
8
|
+
import { countEntities, readLocalSchemaFile } from '../old.js';
|
|
9
|
+
import { UI } from '../ui/index.ts';
|
|
10
|
+
import { getSchemaPathToWrite } from '../util/findConfigCandidates.ts';
|
|
11
|
+
import { CurrentApp } from '../context/currentApp.ts';
|
|
12
|
+
import { ProjectInfo } from '../context/projectInfo.ts';
|
|
13
|
+
import { InstantHttpAuthed, withCommand } from './http.ts';
|
|
14
|
+
import { promptOk } from './ui.ts';
|
|
15
|
+
import { ReadSchemaFileError } from './pushSchema.ts';
|
|
16
|
+
import { mergeSchema, MergeSchemaError } from '../util/mergeSchema.ts';
|
|
17
|
+
|
|
18
|
+
export const pullSchema = ({
|
|
19
|
+
experimentalTypePreservation,
|
|
20
|
+
}: {
|
|
21
|
+
experimentalTypePreservation?: boolean;
|
|
22
|
+
}) =>
|
|
23
|
+
Effect.gen(function* () {
|
|
24
|
+
yield* Effect.log('Pulling schema...');
|
|
25
|
+
const { appId } = yield* CurrentApp;
|
|
26
|
+
const http = yield* InstantHttpAuthed;
|
|
27
|
+
|
|
28
|
+
const schemaResponse = yield* http
|
|
29
|
+
.pipe(withCommand('pull'))
|
|
30
|
+
.get(`/dash/apps/${appId}/schema/pull`)
|
|
31
|
+
.pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(Schema.Any))); // parse result body into "any"
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
!countEntities(schemaResponse.schema.refs) &&
|
|
35
|
+
!countEntities(schemaResponse.schema.blobs)
|
|
36
|
+
) {
|
|
37
|
+
yield* Effect.log('Schema is empty. Skipping.');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const prevSchemaFile = yield* Effect.tryPromise(readLocalSchemaFile).pipe(
|
|
42
|
+
Effect.mapError((err) =>
|
|
43
|
+
ReadSchemaFileError.make({
|
|
44
|
+
message: `Error reading local schema file: ${err}`,
|
|
45
|
+
cause: err,
|
|
46
|
+
}),
|
|
47
|
+
),
|
|
48
|
+
);
|
|
49
|
+
const shortSchemaPath = getSchemaPathToWrite(prevSchemaFile?.path);
|
|
50
|
+
const path = yield* Path.Path;
|
|
51
|
+
const { pkgDir, instantModuleName } = yield* ProjectInfo;
|
|
52
|
+
const schemaPath = path.join(pkgDir, shortSchemaPath);
|
|
53
|
+
|
|
54
|
+
if (prevSchemaFile) {
|
|
55
|
+
const shouldContinue = yield* promptOk({
|
|
56
|
+
promptText: `This will overwrite your local ${shortSchemaPath} file, OK to proceed?`,
|
|
57
|
+
modifyOutput: UI.modifiers.yPadding,
|
|
58
|
+
inline: true,
|
|
59
|
+
});
|
|
60
|
+
if (!shouldContinue) return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let newSchemaContent = generateSchemaTypescriptFile(
|
|
64
|
+
prevSchemaFile?.schema,
|
|
65
|
+
apiSchemaToInstantSchemaDef(schemaResponse.schema),
|
|
66
|
+
instantModuleName,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (prevSchemaFile && experimentalTypePreservation) {
|
|
70
|
+
const fs = yield* FileSystem.FileSystem;
|
|
71
|
+
const oldSchemaContent = yield* fs.readFileString(prevSchemaFile.path);
|
|
72
|
+
newSchemaContent = yield* Effect.try(() =>
|
|
73
|
+
mergeSchema(oldSchemaContent, newSchemaContent),
|
|
74
|
+
).pipe(
|
|
75
|
+
Effect.mapError((e) => new MergeSchemaError({ message: e.message })),
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
yield* writeTypescript(schemaPath, newSchemaContent);
|
|
79
|
+
yield* Effect.log('✅ Wrote schema to ' + shortSchemaPath);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
export const writeTypescript = (path: string, content: string) =>
|
|
83
|
+
Effect.gen(function* () {
|
|
84
|
+
const prettierConfig = yield* Effect.tryPromise(() =>
|
|
85
|
+
prettier.resolveConfig(path),
|
|
86
|
+
);
|
|
87
|
+
const formattedCode = yield* Effect.tryPromise(() =>
|
|
88
|
+
prettier.format(content, {
|
|
89
|
+
...prettierConfig,
|
|
90
|
+
parser: 'typescript',
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
const fs = yield* FileSystem.FileSystem;
|
|
94
|
+
yield* fs.writeFileString(path, formattedCode);
|
|
95
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Effect, Option, Schema } from 'effect';
|
|
2
|
+
import jsonDiff from 'json-diff';
|
|
3
|
+
import { readLocalPermsFile } from '../old.js';
|
|
4
|
+
import { InstantHttpAuthed, withCommand } from './http.ts';
|
|
5
|
+
import {
|
|
6
|
+
HttpClient,
|
|
7
|
+
HttpClientRequest,
|
|
8
|
+
HttpClientResponse,
|
|
9
|
+
} from '@effect/platform';
|
|
10
|
+
import { CurrentApp } from '../context/currentApp.ts';
|
|
11
|
+
import { promptOk } from './ui.ts';
|
|
12
|
+
import boxen from 'boxen';
|
|
13
|
+
import chalk from 'chalk';
|
|
14
|
+
|
|
15
|
+
export class NoPermsFileError extends Schema.TaggedError<NoPermsFileError>(
|
|
16
|
+
'NoPermsFileError',
|
|
17
|
+
)('NoPermsFileError', {
|
|
18
|
+
message: Schema.String,
|
|
19
|
+
}) {}
|
|
20
|
+
|
|
21
|
+
const PullPermsResponse = Schema.Struct({
|
|
22
|
+
perms: Schema.Any.pipe(Schema.optional),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const pushPerms = Effect.gen(function* () {
|
|
26
|
+
yield* Effect.log('Planning perms...');
|
|
27
|
+
const { appId } = yield* CurrentApp;
|
|
28
|
+
const http = yield* InstantHttpAuthed;
|
|
29
|
+
|
|
30
|
+
const permsFile = yield* Effect.tryPromise(readLocalPermsFile).pipe(
|
|
31
|
+
Effect.flatMap(Option.fromNullable),
|
|
32
|
+
Effect.mapError(() =>
|
|
33
|
+
NoPermsFileError.make({
|
|
34
|
+
message: `We couldn't find your ${chalk.yellow('`instant.perms.ts`')} file. Make sure it's in the root directory. (Hint: You can use an INSTANT_PERMS_FILE_PATH environment variable to specify it.)`,
|
|
35
|
+
}),
|
|
36
|
+
),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const prodPerms = yield* http
|
|
40
|
+
.pipe(withCommand('push'))
|
|
41
|
+
.get(`/dash/apps/${appId}/perms/pull`)
|
|
42
|
+
.pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(PullPermsResponse))); // parse result body into "any"
|
|
43
|
+
|
|
44
|
+
const diffedStr = jsonDiff.diffString(
|
|
45
|
+
prodPerms.perms || {},
|
|
46
|
+
permsFile.perms || {},
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (!diffedStr.length) {
|
|
50
|
+
yield* Effect.log('No perms changes detected. Skipping.');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const okPush = yield* promptOk({
|
|
55
|
+
promptText: 'Push these changes to your perms?',
|
|
56
|
+
modifyOutput: (output) => {
|
|
57
|
+
let both = diffedStr + '\n' + output;
|
|
58
|
+
return boxen(both, {
|
|
59
|
+
dimBorder: true,
|
|
60
|
+
padding: {
|
|
61
|
+
left: 1,
|
|
62
|
+
right: 1,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
if (!okPush) return;
|
|
68
|
+
|
|
69
|
+
yield* http
|
|
70
|
+
.pipe(
|
|
71
|
+
withCommand('push'),
|
|
72
|
+
HttpClient.mapRequestInputEffect(
|
|
73
|
+
HttpClientRequest.bodyJson({ code: permsFile.perms }),
|
|
74
|
+
),
|
|
75
|
+
)
|
|
76
|
+
.post(`/dash/apps/${appId}/rules`)
|
|
77
|
+
.pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(Schema.Any)));
|
|
78
|
+
|
|
79
|
+
yield* Effect.log(chalk.green('Permissions updated!'));
|
|
80
|
+
});
|