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.
Files changed (213) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/__tests__/e2e/cli.e2e.test.ts +3 -3
  3. package/__tests__/e2e/helpers.ts +1 -1
  4. package/__tests__/effectHelpers.ts +45 -0
  5. package/__tests__/mergeSchema.test.ts +2 -2
  6. package/dist/commands/claim.d.ts +6 -0
  7. package/dist/commands/claim.d.ts.map +1 -0
  8. package/dist/commands/claim.js +22 -0
  9. package/dist/commands/claim.js.map +1 -0
  10. package/dist/commands/explorer.d.ts +6 -0
  11. package/dist/commands/explorer.d.ts.map +1 -0
  12. package/dist/commands/explorer.js +13 -0
  13. package/dist/commands/explorer.js.map +1 -0
  14. package/dist/commands/info.d.ts +3 -0
  15. package/dist/commands/info.d.ts.map +1 -0
  16. package/dist/commands/info.js +24 -0
  17. package/dist/commands/info.js.map +1 -0
  18. package/dist/commands/init.d.ts +5 -0
  19. package/dist/commands/init.d.ts.map +1 -0
  20. package/dist/commands/init.js +39 -0
  21. package/dist/commands/init.js.map +1 -0
  22. package/dist/commands/initWithoutFiles.d.ts +6 -0
  23. package/dist/commands/initWithoutFiles.d.ts.map +1 -0
  24. package/dist/commands/initWithoutFiles.js +64 -0
  25. package/dist/commands/initWithoutFiles.js.map +1 -0
  26. package/dist/commands/login.d.ts +9 -0
  27. package/dist/commands/login.d.ts.map +1 -0
  28. package/dist/commands/login.js +52 -0
  29. package/dist/commands/login.js.map +1 -0
  30. package/dist/commands/logout.d.ts +4 -0
  31. package/dist/commands/logout.d.ts.map +1 -0
  32. package/dist/commands/logout.js +21 -0
  33. package/dist/commands/logout.js.map +1 -0
  34. package/dist/commands/pull.d.ts +6 -0
  35. package/dist/commands/pull.d.ts.map +1 -0
  36. package/dist/commands/pull.js +16 -0
  37. package/dist/commands/pull.js.map +1 -0
  38. package/dist/commands/push.d.ts +6 -0
  39. package/dist/commands/push.d.ts.map +1 -0
  40. package/dist/commands/push.js +20 -0
  41. package/dist/commands/push.js.map +1 -0
  42. package/dist/commands/query.d.ts +7 -0
  43. package/dist/commands/query.d.ts.map +1 -0
  44. package/dist/commands/query.js +52 -0
  45. package/dist/commands/query.js.map +1 -0
  46. package/dist/context/authToken.d.ts +30 -0
  47. package/dist/context/authToken.d.ts.map +1 -0
  48. package/dist/context/authToken.js +86 -0
  49. package/dist/context/authToken.js.map +1 -0
  50. package/dist/context/currentApp.d.ts +37 -0
  51. package/dist/context/currentApp.d.ts.map +1 -0
  52. package/dist/context/currentApp.js +204 -0
  53. package/dist/context/currentApp.js.map +1 -0
  54. package/dist/context/globalOpts.d.ts +11 -0
  55. package/dist/context/globalOpts.d.ts.map +1 -0
  56. package/dist/context/globalOpts.js +13 -0
  57. package/dist/context/globalOpts.js.map +1 -0
  58. package/dist/context/platformApi.d.ts +19 -0
  59. package/dist/context/platformApi.d.ts.map +1 -0
  60. package/dist/context/platformApi.js +24 -0
  61. package/dist/context/platformApi.js.map +1 -0
  62. package/dist/context/projectInfo.d.ts +29 -0
  63. package/dist/context/projectInfo.d.ts.map +1 -0
  64. package/dist/context/projectInfo.js +149 -0
  65. package/dist/context/projectInfo.js.map +1 -0
  66. package/dist/errors.d.ts +10 -0
  67. package/dist/errors.d.ts.map +1 -0
  68. package/dist/errors.js +6 -0
  69. package/dist/errors.js.map +1 -0
  70. package/dist/index.d.ts +41 -7
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +169 -1781
  73. package/dist/index.js.map +1 -1
  74. package/dist/layer.d.ts +23 -0
  75. package/dist/layer.d.ts.map +1 -0
  76. package/dist/layer.js +68 -0
  77. package/dist/layer.js.map +1 -0
  78. package/dist/lib/createApp.d.ts +12 -0
  79. package/dist/lib/createApp.d.ts.map +1 -0
  80. package/dist/lib/createApp.js +15 -0
  81. package/dist/lib/createApp.js.map +1 -0
  82. package/dist/lib/handleEnv.d.ts +7 -0
  83. package/dist/lib/handleEnv.d.ts.map +1 -0
  84. package/dist/lib/handleEnv.js +91 -0
  85. package/dist/lib/handleEnv.js.map +1 -0
  86. package/dist/lib/http.d.ts +32 -0
  87. package/dist/lib/http.d.ts.map +1 -0
  88. package/dist/lib/http.js +67 -0
  89. package/dist/lib/http.js.map +1 -0
  90. package/dist/lib/login.d.ts +13 -0
  91. package/dist/lib/login.d.ts.map +1 -0
  92. package/dist/lib/login.js +39 -0
  93. package/dist/lib/login.js.map +1 -0
  94. package/dist/lib/pullPerms.d.ts +7 -0
  95. package/dist/lib/pullPerms.d.ts.map +1 -0
  96. package/dist/lib/pullPerms.js +41 -0
  97. package/dist/lib/pullPerms.js.map +1 -0
  98. package/dist/lib/pullSchema.d.ts +12 -0
  99. package/dist/lib/pullSchema.d.ts.map +1 -0
  100. package/dist/lib/pullSchema.js +62 -0
  101. package/dist/lib/pullSchema.js.map +1 -0
  102. package/dist/lib/pushPerms.d.ts +13 -0
  103. package/dist/lib/pushPerms.d.ts.map +1 -0
  104. package/dist/lib/pushPerms.js +54 -0
  105. package/dist/lib/pushPerms.js.map +1 -0
  106. package/dist/lib/pushSchema.d.ts +53 -0
  107. package/dist/lib/pushSchema.d.ts.map +1 -0
  108. package/dist/lib/pushSchema.js +160 -0
  109. package/dist/lib/pushSchema.js.map +1 -0
  110. package/dist/lib/ui.d.ts +16 -0
  111. package/dist/lib/ui.d.ts.map +1 -0
  112. package/dist/lib/ui.js +22 -0
  113. package/dist/lib/ui.js.map +1 -0
  114. package/dist/logging.d.ts +4 -0
  115. package/dist/logging.d.ts.map +1 -0
  116. package/dist/logging.js +17 -0
  117. package/dist/logging.js.map +1 -0
  118. package/dist/old.d.ts +14 -0
  119. package/dist/old.d.ts.map +1 -0
  120. package/dist/old.js +417 -0
  121. package/dist/old.js.map +1 -0
  122. package/dist/program.d.ts +3 -0
  123. package/dist/program.d.ts.map +1 -0
  124. package/dist/program.js +3 -0
  125. package/dist/program.js.map +1 -0
  126. package/dist/renderSchemaPlan.d.ts +3 -3
  127. package/dist/renderSchemaPlan.d.ts.map +1 -1
  128. package/dist/renderSchemaPlan.js +2 -14
  129. package/dist/renderSchemaPlan.js.map +1 -1
  130. package/dist/ui/index.d.ts +4 -3
  131. package/dist/ui/index.d.ts.map +1 -1
  132. package/dist/ui/index.js +2 -2
  133. package/dist/ui/index.js.map +1 -1
  134. package/dist/ui/lib.js +1 -0
  135. package/dist/ui/lib.js.map +1 -1
  136. package/dist/util/findConfigCandidates.d.ts +1 -1
  137. package/dist/util/findConfigCandidates.d.ts.map +1 -1
  138. package/dist/util/findConfigCandidates.js +1 -3
  139. package/dist/util/findConfigCandidates.js.map +1 -1
  140. package/dist/util/fs.d.ts +1 -1
  141. package/dist/util/fs.d.ts.map +1 -1
  142. package/dist/util/fs.js.map +1 -1
  143. package/dist/util/getAuthPaths.d.ts.map +1 -1
  144. package/dist/util/getAuthPaths.js.map +1 -1
  145. package/dist/util/isHeadlessEnvironment.d.ts +3 -1
  146. package/dist/util/isHeadlessEnvironment.d.ts.map +1 -1
  147. package/dist/util/isHeadlessEnvironment.js.map +1 -1
  148. package/dist/util/loadConfig.d.ts +1 -1
  149. package/dist/util/loadConfig.d.ts.map +1 -1
  150. package/dist/util/loadConfig.js +2 -2
  151. package/dist/util/loadConfig.js.map +1 -1
  152. package/dist/util/mergeSchema.d.ts +9 -1
  153. package/dist/util/mergeSchema.d.ts.map +1 -1
  154. package/dist/util/mergeSchema.js +4 -0
  155. package/dist/util/mergeSchema.js.map +1 -1
  156. package/dist/util/renamePrompt.d.ts +2 -1
  157. package/dist/util/renamePrompt.d.ts.map +1 -1
  158. package/dist/util/renamePrompt.js +1 -1
  159. package/dist/util/renamePrompt.js.map +1 -1
  160. package/package.json +17 -7
  161. package/src/commands/claim.ts +31 -0
  162. package/src/commands/explorer.ts +21 -0
  163. package/src/commands/info.ts +34 -0
  164. package/src/commands/init.ts +58 -0
  165. package/src/commands/initWithoutFiles.ts +107 -0
  166. package/src/commands/login.ts +76 -0
  167. package/src/commands/logout.ts +23 -0
  168. package/src/commands/pull.ts +23 -0
  169. package/src/commands/push.ts +25 -0
  170. package/src/commands/query.ts +61 -0
  171. package/src/context/authToken.ts +149 -0
  172. package/src/context/currentApp.ts +277 -0
  173. package/src/context/globalOpts.ts +22 -0
  174. package/src/context/platformApi.ts +35 -0
  175. package/src/context/projectInfo.ts +215 -0
  176. package/src/errors.ts +7 -0
  177. package/src/index.ts +428 -0
  178. package/src/layer.ts +155 -0
  179. package/src/lib/createApp.ts +28 -0
  180. package/src/lib/handleEnv.ts +115 -0
  181. package/src/lib/http.ts +148 -0
  182. package/src/lib/login.ts +54 -0
  183. package/src/lib/pullPerms.ts +50 -0
  184. package/src/lib/pullSchema.ts +95 -0
  185. package/src/lib/pushPerms.ts +80 -0
  186. package/src/lib/pushSchema.ts +240 -0
  187. package/src/lib/ui.ts +36 -0
  188. package/src/logging.ts +32 -0
  189. package/src/old.js +495 -0
  190. package/src/program.ts +3 -0
  191. package/src/renderSchemaPlan.ts +6 -18
  192. package/src/ui/index.ts +4 -3
  193. package/src/util/findConfigCandidates.ts +1 -2
  194. package/src/util/fs.ts +1 -1
  195. package/src/util/getAuthPaths.ts +1 -0
  196. package/src/util/isHeadlessEnvironment.ts +1 -1
  197. package/src/util/loadConfig.ts +3 -6
  198. package/src/util/{mergeSchema.js → mergeSchema.ts} +26 -16
  199. package/src/util/renamePrompt.ts +2 -1
  200. package/tsconfig.build.json +20 -0
  201. package/tsconfig.json +15 -5
  202. package/vitest.config.ts +2 -1
  203. package/dist/util/packageManager.d.ts +0 -3
  204. package/dist/util/packageManager.d.ts.map +0 -1
  205. package/dist/util/packageManager.js +0 -70
  206. package/dist/util/packageManager.js.map +0 -1
  207. package/dist/util/promptOk.d.ts +0 -4
  208. package/dist/util/promptOk.d.ts.map +0 -1
  209. package/dist/util/promptOk.js +0 -18
  210. package/dist/util/promptOk.js.map +0 -1
  211. package/src/index.js +0 -2333
  212. package/src/util/packageManager.js +0 -78
  213. package/src/util/promptOk.ts +0 -26
@@ -0,0 +1,21 @@
1
+ import { Effect } from 'effect';
2
+ import openInBrowser from 'open';
3
+ import { explorerDef } from '../index.ts';
4
+ import type { OptsFromCommand } from '../index.ts';
5
+ import { CurrentApp } from '../context/currentApp.ts';
6
+ import { getDashUrl } from '../lib/http.ts';
7
+
8
+ export const explorerCmd = (_opts: OptsFromCommand<typeof explorerDef>) =>
9
+ Effect.gen(function* () {
10
+ const { appId } = yield* CurrentApp;
11
+ const dashUrl = yield* getDashUrl;
12
+ yield* Effect.log('Opening Explorer...');
13
+ const url = `${dashUrl}/dash?s=main&app=${appId}&t=explorer`;
14
+ yield* Effect.tryPromise(() => openInBrowser(url)).pipe(
15
+ Effect.catchAll(() =>
16
+ Effect.log(
17
+ `Failed to open Explorer in browser\nOpen Explorer manually:\n${url}`,
18
+ ),
19
+ ),
20
+ );
21
+ });
@@ -0,0 +1,34 @@
1
+ import { HttpClientResponse } from '@effect/platform';
2
+ import { Effect, Schema, Option } from 'effect';
3
+ import { InstantHttpAuthed } from '../lib/http.ts';
4
+ import { version } from '@instantdb/version';
5
+
6
+ const DashMeResponse = Schema.Struct({
7
+ user: Schema.Struct({
8
+ id: Schema.String,
9
+ email: Schema.String,
10
+ created_at: Schema.String,
11
+ }),
12
+ });
13
+
14
+ export const infoCommand = () =>
15
+ Effect.gen(function* () {
16
+ const authedHttp = yield* Effect.serviceOption(InstantHttpAuthed).pipe(
17
+ Effect.map(Option.getOrNull),
18
+ );
19
+
20
+ yield* Effect.log('CLI Version:', version);
21
+ // If logged in..
22
+ if (authedHttp) {
23
+ const meData = yield* authedHttp.get('/dash/me').pipe(
24
+ Effect.flatMap(HttpClientResponse.schemaBodyJson(DashMeResponse)),
25
+ Effect.mapError(
26
+ (e) => new Error("Couldn't get user information.", { cause: e }),
27
+ ),
28
+ );
29
+
30
+ yield* Effect.log(`Logged in as ${meData.user.email}`);
31
+ } else {
32
+ yield* Effect.log('Not logged in.');
33
+ }
34
+ });
@@ -0,0 +1,58 @@
1
+ import { Effect, Option } from 'effect';
2
+ import { initDef } from '../index.ts';
3
+ import type { OptsFromCommand } from '../index.ts';
4
+ import { readLocalPermsFile, readLocalSchemaFile } from '../old.js';
5
+ import { pullSchema } from '../lib/pullSchema.ts';
6
+ import { pullPerms } from '../lib/pullPerms.ts';
7
+ import { promptOk } from '../lib/ui.ts';
8
+ import { pushSchema } from '../lib/pushSchema.ts';
9
+ import { pushPerms } from '../lib/pushPerms.ts';
10
+
11
+ export const initCommand = (_options: OptsFromCommand<typeof initDef>) =>
12
+ Effect.gen(function* () {
13
+ yield* Effect.matchEffect(
14
+ Effect.tryPromise(readLocalSchemaFile).pipe(
15
+ // Throws NoSuchElementException if no file found
16
+ Effect.flatMap(Option.fromNullable),
17
+ ),
18
+ {
19
+ onFailure: () => pullSchema({ experimentalTypePreservation: false }),
20
+ onSuccess: () =>
21
+ Effect.gen(function* () {
22
+ const doSchemaPush = yield* promptOk(
23
+ {
24
+ promptText: 'Found local schema. Push it to the new app?',
25
+ inline: true,
26
+ },
27
+ true,
28
+ );
29
+ if (doSchemaPush) {
30
+ yield* pushSchema();
31
+ }
32
+ }),
33
+ },
34
+ );
35
+
36
+ yield* Effect.matchEffect(
37
+ Effect.tryPromise(readLocalPermsFile).pipe(
38
+ // Throws NoSuchElementException if no file found
39
+ Effect.flatMap(Option.fromNullable),
40
+ ),
41
+ {
42
+ onFailure: () => pullPerms,
43
+ onSuccess: () =>
44
+ Effect.gen(function* () {
45
+ const doPermsPush = yield* promptOk(
46
+ {
47
+ promptText: 'Found local perms. Push it to the new app?',
48
+ inline: true,
49
+ },
50
+ true,
51
+ );
52
+ if (doPermsPush) {
53
+ yield* pushPerms;
54
+ }
55
+ }),
56
+ },
57
+ );
58
+ });
@@ -0,0 +1,107 @@
1
+ import { Effect } from 'effect';
2
+ import { PlatformApi } from '../context/platformApi.ts';
3
+ import { BadArgsError } from '../errors.ts';
4
+ import { initWithoutFilesDef } from '../index.ts';
5
+ import type { OptsFromCommand } from '../index.ts';
6
+ import { createApp } from '../lib/createApp.ts';
7
+ import { AuthLayerLive } from '../layer.ts';
8
+ import chalk from 'chalk';
9
+ import { NotAuthedError } from '../context/authToken.ts';
10
+
11
+ export const initWithoutFilesCommand = (
12
+ opts: OptsFromCommand<typeof initWithoutFilesDef>,
13
+ ) =>
14
+ Effect.gen(function* () {
15
+ if (!opts?.title) {
16
+ return yield* BadArgsError.make({
17
+ message:
18
+ 'Title is required for creating a new app without local files.',
19
+ });
20
+ }
21
+
22
+ if (opts.title.startsWith('-')) {
23
+ return yield* BadArgsError.make({
24
+ message: `Invalid title: "${opts.title}". Title cannot be a flag.`,
25
+ });
26
+ }
27
+
28
+ if (opts?.temp && opts?.orgId) {
29
+ return yield* BadArgsError.make({
30
+ message: 'Cannot use --temp and --org-id flags together.',
31
+ });
32
+ }
33
+
34
+ if (opts.temp) {
35
+ const platform = yield* PlatformApi;
36
+ const app = yield* platform.use((api) =>
37
+ api.createTemporaryApp({
38
+ title: opts.title!,
39
+ }),
40
+ );
41
+
42
+ console.error(`${chalk.green('Successfully created new app!')}\n`);
43
+ yield* Effect.log(
44
+ JSON.stringify(
45
+ {
46
+ app: {
47
+ appId: app.app.id,
48
+ adminToken: app.app.adminToken,
49
+ },
50
+ error: null,
51
+ },
52
+ null,
53
+ 2,
54
+ ),
55
+ );
56
+
57
+ return;
58
+ }
59
+
60
+ const app = yield* createApp(opts.title, opts.orgId).pipe(
61
+ Effect.provide(
62
+ AuthLayerLive({
63
+ allowAdminToken: false,
64
+ coerce: false,
65
+ }),
66
+ ),
67
+ );
68
+ // Using console.error so the output is part of error channel and not picked up by jq
69
+ console.error(`${chalk.green('Successfully created new app!')}\n`);
70
+ yield* Effect.log(
71
+ JSON.stringify(
72
+ {
73
+ app: {
74
+ appId: app.app.id,
75
+ adminToken: app.app['admin-token'],
76
+ },
77
+ error: null,
78
+ },
79
+ null,
80
+ 2,
81
+ ),
82
+ );
83
+ }).pipe(
84
+ Effect.catchTag('NotAuthedError', (e) =>
85
+ NotAuthedError.make({
86
+ message:
87
+ 'Please log in first with `instant-cli login` before running this command.',
88
+ }),
89
+ ),
90
+ Effect.catchAll((e) =>
91
+ Effect.gen(function* () {
92
+ yield* Effect.log(
93
+ JSON.stringify(
94
+ {
95
+ app: null,
96
+ error: {
97
+ message: e.message,
98
+ },
99
+ },
100
+ null,
101
+ 2,
102
+ ),
103
+ );
104
+ process.exit(1);
105
+ }),
106
+ ),
107
+ );
@@ -0,0 +1,76 @@
1
+ import chalk from 'chalk';
2
+ import { Effect } from 'effect';
3
+ import openInBrowser from 'open';
4
+ import { loginDef } from '../index.ts';
5
+ import type { OptsFromCommand } from '../index.ts';
6
+ import { getDashUrl } from '../lib/http.ts';
7
+ import {
8
+ getLoginTicketAndSecret,
9
+ saveConfigAuthToken,
10
+ waitForAuthToken,
11
+ } from '../lib/login.ts';
12
+ import { promptOk } from '../lib/ui.ts';
13
+
14
+ const isHeadLessEnvironment = (opts: OptsFromCommand<typeof loginDef>) => {
15
+ const noBrowserMode = Boolean(
16
+ process.env.INSTANT_CLI_NO_BROWSER || process.env.CI || opts?.headless,
17
+ );
18
+
19
+ // Check for common headless environment indicators
20
+ return (
21
+ noBrowserMode ||
22
+ process.env.TERM === 'dumb' ||
23
+ process.env.SSH_CONNECTION !== undefined ||
24
+ process.env.SSH_CLIENT !== undefined ||
25
+ (!process.env.DISPLAY && process.platform === 'linux') ||
26
+ process.env.WSL_DISTRO_NAME !== undefined
27
+ );
28
+ };
29
+
30
+ export const loginCommand = Effect.fn(function* (
31
+ opts: OptsFromCommand<typeof loginDef>,
32
+ ) {
33
+ yield* Effect.log("Let's log you in!");
34
+
35
+ const loginInfo = yield* getLoginTicketAndSecret;
36
+ const { secret, ticket } = loginInfo;
37
+ const dashOrigin = yield* getDashUrl;
38
+ yield* Effect.log();
39
+ // TODO: flip these so rejecting the prompt prints url
40
+ if (isHeadLessEnvironment(opts)) {
41
+ yield* Effect.log(
42
+ `Open this URL in a browser to log in:\n ${dashOrigin}/dash?ticket=${ticket}\n`,
43
+ );
44
+ } else {
45
+ const ok = yield* promptOk(
46
+ {
47
+ promptText:
48
+ 'This will open instantdb.com in your browser, OK to proceed?',
49
+ },
50
+ true,
51
+ );
52
+ if (!ok) {
53
+ process.exit(0);
54
+ }
55
+ yield* Effect.tryPromise(() =>
56
+ openInBrowser(`${dashOrigin}/dash?ticket=${ticket}`),
57
+ );
58
+ }
59
+
60
+ yield* Effect.log('Waiting for authentication...');
61
+
62
+ const result = yield* waitForAuthToken(secret);
63
+ const { token, email } = result;
64
+ if (opts.print) {
65
+ yield* Effect.log(
66
+ chalk.red('[Do not share] Your Instant auth token:', token),
67
+ );
68
+ } else {
69
+ yield* saveConfigAuthToken(token);
70
+ yield* Effect.log(chalk.green(`Successfully logged in as ${email}!`));
71
+ }
72
+ return {
73
+ authToken: token,
74
+ source: 'file' as const,
75
+ };
76
+ });
@@ -0,0 +1,23 @@
1
+ import { Effect } from 'effect';
2
+ import { getAuthPaths } from '../util/getAuthPaths.ts';
3
+ import { FileSystem } from '@effect/platform';
4
+ import chalk from 'chalk';
5
+ import { SystemError } from '@effect/platform/Error';
6
+
7
+ export const logoutCommand = Effect.fn(function* () {
8
+ const { authConfigFilePath } = getAuthPaths();
9
+ const fs = yield* FileSystem.FileSystem;
10
+
11
+ yield* Effect.matchEffect(fs.remove(authConfigFilePath), {
12
+ onFailure: (e) =>
13
+ Effect.gen(function* () {
14
+ if (e instanceof SystemError && e.reason === 'NotFound') {
15
+ yield* Effect.log(chalk.green('You were already logged out!'));
16
+ } else {
17
+ yield* Effect.logError(chalk.red('Failed to logout: ' + e.message));
18
+ }
19
+ }),
20
+ onSuccess: () =>
21
+ Effect.log(chalk.green('Successfully logged out from Instant!')),
22
+ });
23
+ });
@@ -0,0 +1,23 @@
1
+ import { Effect } from 'effect';
2
+ import { pullDef } from '../index.ts';
3
+ import type { OptsFromCommand } from '../index.ts';
4
+ import { pullSchema } from '../lib/pullSchema.ts';
5
+ import { pullPerms } from '../lib/pullPerms.ts';
6
+
7
+ export type SchemaPermsOrBoth = 'schema' | 'perms' | 'all';
8
+
9
+ export const pullCommand = (
10
+ arg: SchemaPermsOrBoth,
11
+ opts: OptsFromCommand<typeof pullDef>,
12
+ ) =>
13
+ Effect.gen(function* () {
14
+ arg ||= 'all';
15
+ if (arg === 'schema' || arg === 'all') {
16
+ yield* pullSchema({
17
+ experimentalTypePreservation: opts.experimentalTypePreservation,
18
+ });
19
+ }
20
+ if (arg === 'perms' || arg === 'all') {
21
+ yield* pullPerms;
22
+ }
23
+ });
@@ -0,0 +1,25 @@
1
+ import { Effect } from 'effect';
2
+ import { BadArgsError } from '../errors.ts';
3
+ import { pushDef } from '../index.ts';
4
+ import type { OptsFromCommand } from '../index.ts';
5
+ import { pushPerms } from '../lib/pushPerms.ts';
6
+ import { pushSchema } from '../lib/pushSchema.ts';
7
+
8
+ export const pushCommand = (
9
+ arg: string | undefined,
10
+ opts: OptsFromCommand<typeof pushDef>,
11
+ ) =>
12
+ Effect.gen(function* () {
13
+ arg ||= 'all';
14
+ if (arg !== 'schema' && arg !== 'perms' && arg !== 'all') {
15
+ return yield* new BadArgsError({
16
+ message: `Invalid argument: ${arg}. Expected one of schema, perms, or all`,
17
+ });
18
+ }
19
+ if (arg === 'schema' || arg === 'all') {
20
+ yield* pushSchema(opts.rename);
21
+ }
22
+ if (arg === 'perms' || arg === 'all') {
23
+ yield* pushPerms;
24
+ }
25
+ });
@@ -0,0 +1,61 @@
1
+ import { Effect, Layer } from 'effect';
2
+ import JSON5 from 'json5';
3
+ import { queryDef } from '../index.ts';
4
+ import type { OptsFromCommand } from '../index.ts';
5
+ import { CurrentApp } from '../context/currentApp.ts';
6
+ import { BadArgsError } from '../errors.ts';
7
+ import { InstantHttpAuthed, withCommand } from '../lib/http.ts';
8
+ import { HttpBody } from '@effect/platform';
9
+ import { WithAppLayer } from '../layer.ts';
10
+
11
+ export const queryCmd = (arg: string, opts: OptsFromCommand<typeof queryDef>) =>
12
+ Effect.gen(function* () {
13
+ const { appId } = yield* CurrentApp;
14
+ const contexts = [
15
+ opts.admin,
16
+ opts.asEmail,
17
+ opts.asGuest,
18
+ opts.asToken,
19
+ ].filter(Boolean);
20
+ if (contexts.length > 1) {
21
+ return yield* BadArgsError.make({
22
+ message:
23
+ 'Please specify exactly one context: --admin, --as-email <email>, --as-guest, or --as-token <token>',
24
+ });
25
+ }
26
+
27
+ const query = yield* Effect.try({
28
+ try: () => JSON5.parse(arg),
29
+ catch: (e) =>
30
+ BadArgsError.make({
31
+ message: String(e),
32
+ }),
33
+ });
34
+
35
+ const headers: Record<string, string> = { 'app-id': appId };
36
+ if (opts.asEmail) {
37
+ headers['as-email'] = opts.asEmail;
38
+ } else if (opts.asGuest) {
39
+ headers['as-guest'] = 'true';
40
+ } else if (opts.asToken) {
41
+ headers['as-token'] = opts.asToken;
42
+ }
43
+
44
+ const http = (yield* InstantHttpAuthed).pipe(withCommand('query'));
45
+ const response = yield* http.post('/admin/query', {
46
+ headers,
47
+ body: HttpBody.unsafeJson({
48
+ query,
49
+ 'inference?': true,
50
+ }),
51
+ });
52
+ const body = yield* response.json;
53
+ yield* Effect.log(JSON.stringify(body, null, 2));
54
+ }).pipe(
55
+ Effect.provide(
56
+ WithAppLayer({
57
+ coerce: false,
58
+ appId: opts.app,
59
+ }).pipe(Layer.annotateLogs('silent', true)),
60
+ ),
61
+ );
@@ -0,0 +1,149 @@
1
+ import { FileSystem } from '@effect/platform';
2
+ import { Config, Context, Effect, Layer, Option, Ref, Schema } from 'effect';
3
+ import envPaths from 'env-paths';
4
+ import { join } from 'path';
5
+ import { loginCommand } from '../commands/login.ts';
6
+ import { program } from '../program.ts';
7
+
8
+ type AuthTokenSource = 'admin' | 'env' | 'opt' | 'file';
9
+
10
+ type AuthTokenState = {
11
+ authToken: string;
12
+ source: AuthTokenSource;
13
+ };
14
+
15
+ export class AuthToken extends Context.Tag('instant-cli/new/context/authToken')<
16
+ AuthToken,
17
+ {
18
+ getAuthToken: Effect.Effect<string>;
19
+ getSource: Effect.Effect<AuthTokenSource>;
20
+ setAuthToken: (
21
+ authToken: string,
22
+ source?: AuthTokenSource,
23
+ ) => Effect.Effect<void>;
24
+ }
25
+ >() {}
26
+
27
+ export class NotAuthedError extends Schema.TaggedError<NotAuthedError>(
28
+ 'NotAuthedError',
29
+ )('NotAuthedError', {
30
+ message: Schema.String,
31
+ }) {}
32
+
33
+ export const authTokenGetEffect = (allowAdminToken: boolean = true) =>
34
+ Effect.gen(function* () {
35
+ const options = program.opts() as Record<string, any>;
36
+ if (typeof options.token === 'string') {
37
+ return {
38
+ authToken: options.token,
39
+ source: 'opt' as 'env' | 'opt' | 'file',
40
+ };
41
+ }
42
+
43
+ const env = yield* Config.string('INSTANT_CLI_AUTH_TOKEN').pipe(
44
+ Config.option,
45
+ Config.map(Option.getOrNull),
46
+ );
47
+
48
+ if (env) {
49
+ return {
50
+ authToken: env,
51
+ source: 'env' as 'env' | 'opt' | 'file',
52
+ };
53
+ }
54
+
55
+ const secondaryEnv = yield* Config.string('INSTANT_APP_ADMIN_TOKEN').pipe(
56
+ Config.orElse(() => Config.string('INSTANT_ADMIN_TOKEN')),
57
+ Config.option,
58
+ Config.map(Option.getOrNull),
59
+ );
60
+ if (secondaryEnv && allowAdminToken) {
61
+ return {
62
+ authToken: secondaryEnv,
63
+ source: 'admin' as 'admin',
64
+ };
65
+ }
66
+
67
+ const authPaths = yield* getAuthPaths;
68
+ const fs = yield* FileSystem.FileSystem;
69
+ const file = yield* fs
70
+ .readFileString(authPaths.authConfigFilePath, 'utf8')
71
+ .pipe(
72
+ // will usually fail if file not found, return null instead
73
+ Effect.orElseSucceed(() => null),
74
+ );
75
+ if (file) {
76
+ return {
77
+ authToken: file,
78
+ source: 'file' as 'env' | 'opt' | 'file',
79
+ };
80
+ }
81
+
82
+ return yield* NotAuthedError.make({ message: 'You are not logged in' });
83
+ });
84
+
85
+ export const AuthTokenLive = ({
86
+ coerce,
87
+ allowAdminToken = true,
88
+ }: {
89
+ coerce: boolean;
90
+ allowAdminToken: boolean;
91
+ }) =>
92
+ Layer.effect(
93
+ AuthToken,
94
+ authTokenGetEffect(allowAdminToken).pipe(
95
+ Effect.flatMap((tokenResult) =>
96
+ Effect.gen(function* () {
97
+ const authStateRef = yield* Ref.make<AuthTokenState>(tokenResult);
98
+ return {
99
+ getAuthToken: Ref.get(authStateRef).pipe(
100
+ Effect.map((authState) => authState.authToken),
101
+ ),
102
+ getSource: Ref.get(authStateRef).pipe(
103
+ Effect.map((authState) => authState.source),
104
+ ),
105
+ setAuthToken: (authToken: string, source?: AuthTokenSource) =>
106
+ Ref.update(authStateRef, (authState) => ({
107
+ ...authState,
108
+ authToken,
109
+ source: source ?? authState.source,
110
+ })),
111
+ };
112
+ }),
113
+ ),
114
+ Effect.catchTag('NotAuthedError', (e) =>
115
+ Effect.gen(function* () {
116
+ if (coerce) {
117
+ const loginResult = yield* loginCommand({});
118
+ const authStateRef = yield* Ref.make<AuthTokenState>(loginResult);
119
+ return {
120
+ getAuthToken: Ref.get(authStateRef).pipe(
121
+ Effect.map((authState) => authState.authToken),
122
+ ),
123
+ getSource: Ref.get(authStateRef).pipe(
124
+ Effect.map((authState) => authState.source),
125
+ ),
126
+ setAuthToken: (authToken: string, source?: AuthTokenSource) =>
127
+ Ref.update(authStateRef, (authState) => ({
128
+ ...authState,
129
+ authToken,
130
+ source: source ?? authState.source,
131
+ })),
132
+ };
133
+ } else {
134
+ return yield* e;
135
+ }
136
+ }),
137
+ ),
138
+ ),
139
+ );
140
+
141
+ const getAuthPaths = Effect.gen(function* () {
142
+ const dev = yield* Config.boolean('INSTANT_CLI_DEV').pipe(
143
+ Config.withDefault(false),
144
+ );
145
+ const key = `instantdb-${dev ? 'dev' : 'prod'}`;
146
+ const { config: appConfigDirPath } = envPaths(key);
147
+ const authConfigFilePath = join(appConfigDirPath, 'a');
148
+ return { authConfigFilePath, appConfigDirPath };
149
+ });