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,277 @@
|
|
|
1
|
+
import { HttpClientRequest, HttpClientResponse } from '@effect/platform';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { Context, Data, Effect, Layer, Runtime, Schema, Option } from 'effect';
|
|
4
|
+
import { UI } from '../ui/index.ts';
|
|
5
|
+
import { handleEnv } from '../lib/handleEnv.ts';
|
|
6
|
+
import { getBaseUrl, InstantHttpAuthed, withCommand } from '../lib/http.ts';
|
|
7
|
+
import { runUIEffect } from '../lib/ui.ts';
|
|
8
|
+
import { AuthToken } from './authToken.ts';
|
|
9
|
+
import { GlobalOpts } from './globalOpts.ts';
|
|
10
|
+
import { PlatformApi } from './platformApi.ts';
|
|
11
|
+
import { readInstantConfigFile } from '../old.js';
|
|
12
|
+
import { BadArgsError } from '../errors.ts';
|
|
13
|
+
|
|
14
|
+
export type CurrentAppInfo = {
|
|
15
|
+
appId: string;
|
|
16
|
+
adminToken?: string;
|
|
17
|
+
source: 'create' | 'import' | 'env' | 'flag' | 'ephemeral';
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export class CurrentApp extends Context.Tag(
|
|
21
|
+
'instant-cli/new/context/currentApp',
|
|
22
|
+
)<CurrentApp, CurrentAppInfo>() {}
|
|
23
|
+
|
|
24
|
+
function isUUID(uuid: string) {
|
|
25
|
+
const uuidRegex =
|
|
26
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
27
|
+
return uuidRegex.test(uuid);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class CurrentAppContextError extends Data.TaggedError(
|
|
31
|
+
'CurrentAppContextError',
|
|
32
|
+
)<{
|
|
33
|
+
message: string;
|
|
34
|
+
}> {}
|
|
35
|
+
|
|
36
|
+
export class AppNotFoundError extends Data.TaggedError('AppNotFoundError')<{
|
|
37
|
+
message: string;
|
|
38
|
+
}> {}
|
|
39
|
+
|
|
40
|
+
export const potentialEnvs: Record<string, string> = {
|
|
41
|
+
catchall: 'INSTANT_APP_ID',
|
|
42
|
+
next: 'NEXT_PUBLIC_INSTANT_APP_ID',
|
|
43
|
+
svelte: 'PUBLIC_INSTANT_APP_ID',
|
|
44
|
+
vite: 'VITE_INSTANT_APP_ID',
|
|
45
|
+
expo: 'EXPO_PUBLIC_INSTANT_APP_ID',
|
|
46
|
+
nuxt: 'NUXT_PUBLIC_INSTANT_APP_ID',
|
|
47
|
+
bun: 'BUN_PUBLIC_INSTANT_APP_ID',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const potentialAdminTokenEnvs = {
|
|
51
|
+
default: 'INSTANT_APP_ADMIN_TOKEN',
|
|
52
|
+
short: 'INSTANT_ADMIN_TOKEN',
|
|
53
|
+
} as const;
|
|
54
|
+
|
|
55
|
+
// TODO: add instant.config.ts support
|
|
56
|
+
export const CurrentAppLive = (args: {
|
|
57
|
+
appId?: string;
|
|
58
|
+
coerce?: boolean;
|
|
59
|
+
title?: string;
|
|
60
|
+
applyEnv?: boolean;
|
|
61
|
+
}) =>
|
|
62
|
+
Layer.effect(
|
|
63
|
+
CurrentApp,
|
|
64
|
+
Effect.gen(function* () {
|
|
65
|
+
if (args.appId) {
|
|
66
|
+
if (isUUID(args.appId)) {
|
|
67
|
+
return {
|
|
68
|
+
appId: args.appId,
|
|
69
|
+
source: 'flag' as const,
|
|
70
|
+
};
|
|
71
|
+
} else {
|
|
72
|
+
// Check for instant.config.ts
|
|
73
|
+
const config = yield* Effect.tryPromise(() =>
|
|
74
|
+
readInstantConfigFile(),
|
|
75
|
+
);
|
|
76
|
+
if (!config) {
|
|
77
|
+
return yield* new BadArgsError({
|
|
78
|
+
message: `App ID provided (${args.appId}) is not valid UUID`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const nameMatch = config?.apps?.[args.appId];
|
|
82
|
+
if (!nameMatch?.id) {
|
|
83
|
+
return yield* BadArgsError.make({
|
|
84
|
+
message: 'App ID not found in app inside instant.config.ts',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (nameMatch) {
|
|
88
|
+
return {
|
|
89
|
+
appId: nameMatch.id,
|
|
90
|
+
source: 'flag' as const,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Detect from ENV
|
|
97
|
+
const found = Object.keys(potentialEnvs)
|
|
98
|
+
.map((type) => {
|
|
99
|
+
const envName = potentialEnvs[type];
|
|
100
|
+
const value = process.env[envName];
|
|
101
|
+
return { type, envName, value };
|
|
102
|
+
})
|
|
103
|
+
.find(({ value }) => !!value);
|
|
104
|
+
|
|
105
|
+
if (found?.value && !isUUID(found.value)) {
|
|
106
|
+
return yield* new CurrentAppContextError({
|
|
107
|
+
message: `Invalid UUID: ${found.value}`,
|
|
108
|
+
});
|
|
109
|
+
} else if (found?.value) {
|
|
110
|
+
// try to get admin token
|
|
111
|
+
const adminToken = yield* getAdminToken;
|
|
112
|
+
return {
|
|
113
|
+
adminToken: Option.getOrUndefined(adminToken),
|
|
114
|
+
appId: found?.value,
|
|
115
|
+
source: 'env' as const,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return yield* new AppNotFoundError({
|
|
119
|
+
message: 'No app found',
|
|
120
|
+
});
|
|
121
|
+
}).pipe(
|
|
122
|
+
// coerce into new app if app not found
|
|
123
|
+
Effect.catchTag('AppNotFoundError', () =>
|
|
124
|
+
Effect.gen(function* () {
|
|
125
|
+
if (!args.coerce)
|
|
126
|
+
return yield* new AppNotFoundError({ message: 'No app found' });
|
|
127
|
+
|
|
128
|
+
// coerce into a new app
|
|
129
|
+
const globalOpts = yield* GlobalOpts;
|
|
130
|
+
if (globalOpts.yes) {
|
|
131
|
+
if (!args.title) {
|
|
132
|
+
return yield* new CurrentAppContextError({
|
|
133
|
+
message: `If you provide --yes, you must provide a --title flag.`,
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
yield* Effect.log(`Creating app with title: ${args.title}`);
|
|
137
|
+
return yield* createApp(args.title);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return yield* promptImportOrCreateApp;
|
|
142
|
+
}),
|
|
143
|
+
),
|
|
144
|
+
|
|
145
|
+
// Handle save env
|
|
146
|
+
Effect.tap((app) =>
|
|
147
|
+
Effect.gen(function* () {
|
|
148
|
+
if (
|
|
149
|
+
args.applyEnv &&
|
|
150
|
+
(app.source === 'import' ||
|
|
151
|
+
app.source === 'create' ||
|
|
152
|
+
app.source == 'ephemeral')
|
|
153
|
+
) {
|
|
154
|
+
yield* handleEnv(app);
|
|
155
|
+
}
|
|
156
|
+
}),
|
|
157
|
+
),
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const createApp = Effect.fn(function* (title: string, orgId?: string) {
|
|
162
|
+
const id = randomUUID();
|
|
163
|
+
const token = randomUUID();
|
|
164
|
+
const app = { id, title, admin_token: token, org_id: orgId };
|
|
165
|
+
|
|
166
|
+
const http = yield* InstantHttpAuthed;
|
|
167
|
+
yield* HttpClientRequest.post('/dash/apps').pipe(
|
|
168
|
+
HttpClientRequest.bodyJson(app),
|
|
169
|
+
Effect.flatMap(http.execute),
|
|
170
|
+
);
|
|
171
|
+
return {
|
|
172
|
+
appId: id,
|
|
173
|
+
source: 'create',
|
|
174
|
+
adminToken: token,
|
|
175
|
+
} satisfies CurrentAppInfo;
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const promptImportOrCreateApp = Effect.gen(function* () {
|
|
179
|
+
const api = yield* getSimpleApi;
|
|
180
|
+
const result = yield* runUIEffect(
|
|
181
|
+
new UI.AppSelector({
|
|
182
|
+
startingMenuIndex: 0,
|
|
183
|
+
allowEphemeral: true,
|
|
184
|
+
allowCreate: true,
|
|
185
|
+
api,
|
|
186
|
+
}),
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (result.approach === 'ephemeral') {
|
|
190
|
+
const authToken = yield* AuthToken;
|
|
191
|
+
yield* authToken.setAuthToken(result.adminToken, 'admin');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (result.approach === 'import') {
|
|
195
|
+
yield* Effect.fork(trackAppImport(result.appId));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
appId: result.appId,
|
|
200
|
+
source: result.approach,
|
|
201
|
+
adminToken: result.adminToken,
|
|
202
|
+
} satisfies CurrentAppInfo;
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const getSimpleApi = Effect.gen(function* () {
|
|
206
|
+
const effectRuntime = yield* Effect.runtime<never>();
|
|
207
|
+
|
|
208
|
+
const http = yield* InstantHttpAuthed;
|
|
209
|
+
const dashData = yield* http
|
|
210
|
+
.get('/dash')
|
|
211
|
+
.pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(Schema.Any)));
|
|
212
|
+
const platform = yield* PlatformApi;
|
|
213
|
+
|
|
214
|
+
const baseUrl = yield* getBaseUrl;
|
|
215
|
+
const authToken = yield* AuthToken;
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
getDash: () => dashData,
|
|
219
|
+
createApp: async (title, orgId) => {
|
|
220
|
+
return Runtime.runPromise(
|
|
221
|
+
effectRuntime,
|
|
222
|
+
createApp(title, orgId).pipe(
|
|
223
|
+
Effect.provideService(InstantHttpAuthed, http),
|
|
224
|
+
),
|
|
225
|
+
);
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
createEphemeralApp: async (title) => {
|
|
229
|
+
return await Runtime.runPromise(
|
|
230
|
+
effectRuntime,
|
|
231
|
+
Effect.gen(function* () {
|
|
232
|
+
const platform = yield* PlatformApi;
|
|
233
|
+
const response = yield* platform.use(
|
|
234
|
+
(p) => p.createTemporaryApp({ title: title }),
|
|
235
|
+
'Error creating temporary app',
|
|
236
|
+
);
|
|
237
|
+
return {
|
|
238
|
+
appId: response.app.id,
|
|
239
|
+
adminToken: response.app.adminToken,
|
|
240
|
+
};
|
|
241
|
+
}).pipe(Effect.provideService(PlatformApi, platform)),
|
|
242
|
+
);
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
async getAppsForOrg(orgId) {
|
|
246
|
+
const token = await Runtime.runPromise(
|
|
247
|
+
effectRuntime,
|
|
248
|
+
authToken.getAuthToken,
|
|
249
|
+
);
|
|
250
|
+
const response = await fetch(baseUrl + '/dash/orgs/' + orgId, {
|
|
251
|
+
headers: {
|
|
252
|
+
Authorization: `Bearer ${token}`,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
const data = await response.json();
|
|
256
|
+
return { apps: data.apps };
|
|
257
|
+
},
|
|
258
|
+
} satisfies UI.AppSelectorApi;
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const getAdminToken = Effect.gen(function* () {
|
|
262
|
+
const found = Object.keys(potentialAdminTokenEnvs)
|
|
263
|
+
.map((type) => {
|
|
264
|
+
const envName =
|
|
265
|
+
potentialAdminTokenEnvs[type as keyof typeof potentialAdminTokenEnvs];
|
|
266
|
+
const value = process.env[envName];
|
|
267
|
+
return { type, envName, value };
|
|
268
|
+
})
|
|
269
|
+
.find(({ value }) => !!value);
|
|
270
|
+
return Option.fromNullable(found?.value);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const trackAppImport = (appId: string) =>
|
|
274
|
+
Effect.gen(function* () {
|
|
275
|
+
const http = (yield* InstantHttpAuthed).pipe(withCommand('init'));
|
|
276
|
+
yield* http.post(`/dash/apps/${appId}/track-import`);
|
|
277
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Context, Layer } from 'effect';
|
|
2
|
+
import { program } from '../program.ts';
|
|
3
|
+
|
|
4
|
+
export class GlobalOpts extends Context.Tag(
|
|
5
|
+
'instant-cli/new/context/globalOpts',
|
|
6
|
+
)<
|
|
7
|
+
GlobalOpts,
|
|
8
|
+
{
|
|
9
|
+
token?: string;
|
|
10
|
+
yes: boolean;
|
|
11
|
+
env?: string;
|
|
12
|
+
}
|
|
13
|
+
>() {}
|
|
14
|
+
|
|
15
|
+
export const GlobalOptsLive = Layer.sync(GlobalOpts, () => {
|
|
16
|
+
const opts = program.optsWithGlobals() as Record<string, any>;
|
|
17
|
+
return {
|
|
18
|
+
yes: opts?.yes || false,
|
|
19
|
+
token: opts?.token,
|
|
20
|
+
env: opts?.env,
|
|
21
|
+
};
|
|
22
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { PlatformApi as InstantPlatformApi } from '@instantdb/platform';
|
|
2
|
+
import { Data, Effect } from 'effect';
|
|
3
|
+
import { getBaseUrl } from '../lib/http.ts';
|
|
4
|
+
|
|
5
|
+
export class PlatformApiError extends Data.TaggedError('PlatformApiError')<{
|
|
6
|
+
message: string;
|
|
7
|
+
cause: unknown;
|
|
8
|
+
}> {}
|
|
9
|
+
|
|
10
|
+
export class PlatformApi extends Effect.Service<PlatformApi>()(
|
|
11
|
+
'instant-cli/new/context/platformApi',
|
|
12
|
+
{
|
|
13
|
+
effect: Effect.gen(function* () {
|
|
14
|
+
const origin = yield* getBaseUrl;
|
|
15
|
+
const apiClient = new InstantPlatformApi({
|
|
16
|
+
apiURI: origin,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
use: <R>(
|
|
21
|
+
fun: (api: typeof apiClient) => Promise<R>,
|
|
22
|
+
errorMessage?: string,
|
|
23
|
+
) =>
|
|
24
|
+
Effect.tryPromise({
|
|
25
|
+
try: (_signal) => fun(apiClient),
|
|
26
|
+
catch: (e) =>
|
|
27
|
+
new PlatformApiError({
|
|
28
|
+
message: errorMessage || 'Error using platform api',
|
|
29
|
+
cause: e,
|
|
30
|
+
}),
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
}),
|
|
34
|
+
},
|
|
35
|
+
) {}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { Context, Data, Effect, Layer } from 'effect';
|
|
2
|
+
import { detect } from 'package-manager-detector/detect';
|
|
3
|
+
import { readPackage } from 'pkg-types';
|
|
4
|
+
import type { PackageJson } from 'pkg-types';
|
|
5
|
+
import { exec } from 'child_process';
|
|
6
|
+
import { promisify } from 'util';
|
|
7
|
+
import { UI } from '../ui/index.ts';
|
|
8
|
+
import { findProjectDir } from '../util/projectDir.ts';
|
|
9
|
+
import { runUIEffect } from '../lib/ui.ts';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { GlobalOpts } from './globalOpts.ts';
|
|
12
|
+
import { BadArgsError } from '../errors.ts';
|
|
13
|
+
|
|
14
|
+
export class ProjectInfo extends Context.Tag(
|
|
15
|
+
'instant-cli/new/context/projectInfo',
|
|
16
|
+
)<
|
|
17
|
+
ProjectInfo,
|
|
18
|
+
{
|
|
19
|
+
pkgDir: string;
|
|
20
|
+
projectType: 'node' | 'deno';
|
|
21
|
+
instantModuleName: string;
|
|
22
|
+
}
|
|
23
|
+
>() {}
|
|
24
|
+
|
|
25
|
+
const execAsync = promisify(exec);
|
|
26
|
+
|
|
27
|
+
export const PACKAGE_ALIAS_AND_FULL_NAMES = {
|
|
28
|
+
react: '@instantdb/react',
|
|
29
|
+
'react-native': '@instantdb/react-native',
|
|
30
|
+
core: '@instantdb/core',
|
|
31
|
+
admin: '@instantdb/admin',
|
|
32
|
+
solid: '@instantdb/solidjs',
|
|
33
|
+
svelte: '@instantdb/svelte',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export class ProjectInfoError extends Data.TaggedError('ProjectInfoError')<{
|
|
37
|
+
message: string;
|
|
38
|
+
cause?: unknown;
|
|
39
|
+
}> {}
|
|
40
|
+
|
|
41
|
+
const getProjectInfo = (
|
|
42
|
+
coerce: boolean = true,
|
|
43
|
+
packageName?: keyof typeof PACKAGE_ALIAS_AND_FULL_NAMES,
|
|
44
|
+
) =>
|
|
45
|
+
Effect.gen(function* () {
|
|
46
|
+
const projectDir = yield* Effect.tryPromise({
|
|
47
|
+
try: () => findProjectDir(),
|
|
48
|
+
catch: (e) =>
|
|
49
|
+
new ProjectInfoError({ message: "Couldn't get project dir" }),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!projectDir) {
|
|
53
|
+
return yield* new ProjectInfoError({
|
|
54
|
+
message:
|
|
55
|
+
"Couldn't find a project directory (package.json). Is there a package.json or deno.json file?",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (projectDir.type === 'deno') {
|
|
60
|
+
return {
|
|
61
|
+
pkgDir: projectDir.dir,
|
|
62
|
+
projectType: projectDir.type,
|
|
63
|
+
instantModuleName: '@instantdb/core',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const pkgJson = yield* Effect.tryPromise({
|
|
68
|
+
try: () => readPackage(),
|
|
69
|
+
catch: () =>
|
|
70
|
+
new ProjectInfoError({
|
|
71
|
+
message:
|
|
72
|
+
"We couldn't find an Instant SDK. Install one, or run `init`",
|
|
73
|
+
}),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
yield* Effect.log('Checking for an Instant SDK...');
|
|
77
|
+
let moduleName = getInstantModuleName(pkgJson);
|
|
78
|
+
if (!moduleName && !coerce) {
|
|
79
|
+
return yield* new ProjectInfoError({
|
|
80
|
+
message: "We couldn't find an Instant SDK. Install one, or run `init`",
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!moduleName && coerce) {
|
|
85
|
+
yield* Effect.log(
|
|
86
|
+
"Couldn't find an Instant SDK in your package.json, let's install one!",
|
|
87
|
+
);
|
|
88
|
+
} else {
|
|
89
|
+
yield* Effect.log(
|
|
90
|
+
`Found ${chalk.green(moduleName)} in your package.json.\n`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// TODO: Clean up with option
|
|
95
|
+
const packageManager = yield* Effect.tryPromise(() => detect()).pipe(
|
|
96
|
+
Effect.flatMap(Effect.fromNullable),
|
|
97
|
+
Effect.catchTag('NoSuchElementException', () =>
|
|
98
|
+
Effect.succeed({
|
|
99
|
+
name: 'npm',
|
|
100
|
+
agent: 'npm',
|
|
101
|
+
}),
|
|
102
|
+
),
|
|
103
|
+
Effect.option,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const { yes } = yield* GlobalOpts;
|
|
107
|
+
if (!moduleName && coerce) {
|
|
108
|
+
// install the packages
|
|
109
|
+
if (packageName) {
|
|
110
|
+
moduleName = PACKAGE_ALIAS_AND_FULL_NAMES[packageName];
|
|
111
|
+
} else {
|
|
112
|
+
if (yes) {
|
|
113
|
+
return yield* BadArgsError.make({
|
|
114
|
+
message:
|
|
115
|
+
'--yes was provided without a package specification and no Instant SDK was found',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
moduleName = yield* runUIEffect(
|
|
119
|
+
new UI.Select({
|
|
120
|
+
promptText: 'Which package would you like to use?',
|
|
121
|
+
options: [
|
|
122
|
+
{ label: '@instantdb/react', value: '@instantdb/react' },
|
|
123
|
+
{
|
|
124
|
+
label: '@instantdb/react-native',
|
|
125
|
+
value: '@instantdb/react-native',
|
|
126
|
+
},
|
|
127
|
+
{ label: '@instantdb/core', value: '@instantdb/core' },
|
|
128
|
+
{ label: '@instantdb/admin', value: '@instantdb/admin' },
|
|
129
|
+
{ label: '@instantdb/solidjs', value: '@instantdb/solidjs' },
|
|
130
|
+
{ label: '@instantdb/svelte', value: '@instantdb/svelte' },
|
|
131
|
+
],
|
|
132
|
+
}),
|
|
133
|
+
).pipe(
|
|
134
|
+
Effect.flatMap(Effect.fromNullable),
|
|
135
|
+
Effect.mapError(
|
|
136
|
+
() =>
|
|
137
|
+
new ProjectInfoError({
|
|
138
|
+
message: 'Failed to select package',
|
|
139
|
+
}),
|
|
140
|
+
),
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
const packagesToInstall = [moduleName];
|
|
144
|
+
if (moduleName === '@instantdb/react-native') {
|
|
145
|
+
packagesToInstall.push(
|
|
146
|
+
'react-native-get-random-values',
|
|
147
|
+
'@react-native-async-storage/async-storage',
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const pkgManager = yield* packageManager.pipe(
|
|
152
|
+
Effect.mapError(
|
|
153
|
+
(e) =>
|
|
154
|
+
new ProjectInfoError({
|
|
155
|
+
message: 'Failed to detect package manager',
|
|
156
|
+
cause: e,
|
|
157
|
+
}),
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const installCommand = getInstallCommand(
|
|
162
|
+
pkgManager.agent,
|
|
163
|
+
packagesToInstall.join(' '),
|
|
164
|
+
);
|
|
165
|
+
yield* Effect.log(installCommand);
|
|
166
|
+
yield* runUIEffect(
|
|
167
|
+
new UI.Spinner({
|
|
168
|
+
promise: execAsync(installCommand, {
|
|
169
|
+
cwd: projectDir.dir,
|
|
170
|
+
}),
|
|
171
|
+
errorText: 'Failed to install packages',
|
|
172
|
+
workingText: `Installing ${packagesToInstall.join(', ')} using ${pkgManager.agent}...`,
|
|
173
|
+
doneText: `Installed ${packagesToInstall.join(', ')} using ${pkgManager.agent}.`,
|
|
174
|
+
}),
|
|
175
|
+
);
|
|
176
|
+
return {
|
|
177
|
+
pkgDir: projectDir.dir,
|
|
178
|
+
projectType: projectDir.type,
|
|
179
|
+
instantModuleName: moduleName,
|
|
180
|
+
};
|
|
181
|
+
} else {
|
|
182
|
+
return {
|
|
183
|
+
pkgDir: projectDir.dir,
|
|
184
|
+
projectType: projectDir.type,
|
|
185
|
+
instantModuleName: moduleName!,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
export const ProjectInfoLive = (
|
|
191
|
+
coerce: boolean = true,
|
|
192
|
+
packageName?: keyof typeof PACKAGE_ALIAS_AND_FULL_NAMES,
|
|
193
|
+
) => Layer.effect(ProjectInfo, getProjectInfo(coerce, packageName));
|
|
194
|
+
|
|
195
|
+
function getInstantModuleName(pkgJson: PackageJson) {
|
|
196
|
+
const deps = pkgJson.dependencies || {};
|
|
197
|
+
const devDeps = pkgJson.devDependencies || {};
|
|
198
|
+
const instantModuleName = [
|
|
199
|
+
'@instantdb/react',
|
|
200
|
+
'@instantdb/react-native',
|
|
201
|
+
'@instantdb/core',
|
|
202
|
+
'@instantdb/admin',
|
|
203
|
+
'@instantdb/solidjs',
|
|
204
|
+
'@instantdb/svelte',
|
|
205
|
+
].find((name) => deps[name] || devDeps[name]);
|
|
206
|
+
return instantModuleName;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function getInstallCommand(packageManager: string, moduleName: string) {
|
|
210
|
+
if (packageManager === 'npm') {
|
|
211
|
+
return `npm install ${moduleName}`;
|
|
212
|
+
} else {
|
|
213
|
+
return `${packageManager} add ${moduleName}`;
|
|
214
|
+
}
|
|
215
|
+
}
|