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
package/src/old.js ADDED
@@ -0,0 +1,495 @@
1
+ import boxen from 'boxen';
2
+ import chalk from 'chalk';
3
+ import { program } from '@commander-js/extra-typings';
4
+ import { readFile } from 'fs/promises';
5
+ import path from 'path';
6
+ import terminalLink from 'terminal-link';
7
+ import { UI } from './ui/index.ts';
8
+ import { deferred, renderUnwrap } from './ui/lib.ts';
9
+ import {
10
+ getPermsReadCandidates,
11
+ getSchemaReadCandidates,
12
+ } from './util/findConfigCandidates.ts';
13
+ import { getAuthPaths } from './util/getAuthPaths.ts';
14
+ import { loadConfig } from './util/loadConfig.ts';
15
+ import { loadEnv } from './util/loadEnv.ts';
16
+ import { ResolveRenamePrompt } from './util/renamePrompt.ts';
17
+ import version from './version.js';
18
+
19
+ const dev = Boolean(process.env.INSTANT_CLI_DEV);
20
+ const verbose = Boolean(process.env.INSTANT_CLI_VERBOSE);
21
+
22
+ function error(firstArg, ...rest) {
23
+ console.error(chalk.red('[error]') + ' ' + firstArg, ...rest);
24
+ }
25
+
26
+ const potentialAdminTokenEnvs = {
27
+ default: 'INSTANT_APP_ADMIN_TOKEN',
28
+ short: 'INSTANT_ADMIN_TOKEN',
29
+ };
30
+
31
+ const instantDashOrigin = dev
32
+ ? 'http://localhost:3000'
33
+ : 'https://instantdb.com';
34
+
35
+ const instantBackendOrigin =
36
+ process.env.INSTANT_CLI_API_URI ||
37
+ (dev ? 'http://localhost:8888' : 'https://api.instantdb.com');
38
+
39
+ function indexingJobCompletedActionMessage(job) {
40
+ if (job.job_type === 'check-data-type') {
41
+ return `setting type of ${job.attr_name} to ${job.checked_data_type}`;
42
+ }
43
+ if (job.job_type === 'remove-data-type') {
44
+ return `removing type from ${job.attr_name}`;
45
+ }
46
+ if (job.job_type === 'index') {
47
+ return `adding index to ${job.attr_name}`;
48
+ }
49
+ if (job.job_type === 'remove-index') {
50
+ return `removing index from ${job.attr_name}`;
51
+ }
52
+ if (job.job_type === 'unique') {
53
+ return `adding uniqueness constraint to ${job.attr_name}`;
54
+ }
55
+ if (job.job_type === 'remove-unique') {
56
+ return `removing uniqueness constraint from ${job.attr_name}`;
57
+ }
58
+ if (job.job_type === 'required') {
59
+ return `adding required constraint to ${job.attr_name}`;
60
+ }
61
+ if (job.job_type === 'remove-required') {
62
+ return `removing required constraint from ${job.attr_name}`;
63
+ }
64
+ return `unexpected job type ${job.job_type} - please ping us on discord with this job id (${job.id})`;
65
+ }
66
+
67
+ function truncate(s, maxLen) {
68
+ if (s.length > maxLen) {
69
+ return `${s.substr(0, maxLen - 3)}...`;
70
+ }
71
+ return s;
72
+ }
73
+
74
+ function formatSamples(triples_samples) {
75
+ return triples_samples.slice(0, 3).map((t) => {
76
+ return { ...t, value: truncate(JSON.stringify(t.value), 32) };
77
+ });
78
+ }
79
+
80
+ function createUrl(triple, job) {
81
+ const urlParams = new URLSearchParams({
82
+ s: 'main',
83
+ app: job.app_id,
84
+ t: 'explorer',
85
+ ns: job.attr_name.split('.')[0],
86
+ where: JSON.stringify(['id', triple.entity_id]),
87
+ });
88
+ const url = new URL(instantDashOrigin);
89
+ url.pathname = '/dash';
90
+ url.search = urlParams.toString();
91
+ return url;
92
+ }
93
+
94
+ function padCell(value, width) {
95
+ const trimmed = value.length > width ? value.substring(0, width) : value;
96
+ return trimmed + ' '.repeat(width - trimmed.length);
97
+ }
98
+
99
+ function indexingJobCompletedMessage(job) {
100
+ const actionMessage = indexingJobCompletedActionMessage(job);
101
+ if (job.job_status === 'canceled') {
102
+ return `Canceled ${actionMessage} before it could finish.`;
103
+ }
104
+ if (job.job_status === 'completed') {
105
+ return `Finished ${actionMessage}.`;
106
+ }
107
+ if (job.job_status === 'errored') {
108
+ if (job.invalid_triples_sample?.length) {
109
+ const [etype, label] = job.attr_name.split('.');
110
+ const samples = formatSamples(job.invalid_triples_sample);
111
+ const longestValue = samples.reduce(
112
+ (acc, { value }) => Math.max(acc, value.length),
113
+ label.length,
114
+ );
115
+
116
+ const columns = [
117
+ { header: 'namespace', width: 15, getValue: () => etype },
118
+ {
119
+ header: 'id',
120
+ width: 37,
121
+ getValue: (triple) =>
122
+ terminalLink(triple.entity_id, createUrl(triple, job).toString(), {
123
+ fallback: () => triple.entity_id,
124
+ }),
125
+ },
126
+ {
127
+ header: label,
128
+ width: longestValue + 2,
129
+ getValue: (triple) => triple.value,
130
+ },
131
+ { header: 'type', width: 8, getValue: (triple) => triple.json_type },
132
+ ];
133
+
134
+ let msg = `${chalk.red('INVALID DATA')} ${actionMessage}.\n`;
135
+ if (job.invalid_unique_value) {
136
+ msg += ` Found multiple entities with value ${truncate(JSON.stringify(job.invalid_unique_value), 64)}.\n`;
137
+ }
138
+ if (job.error === 'triple-too-large-error') {
139
+ msg += ` Some of the existing data is too large to index.\n`;
140
+ }
141
+
142
+ msg += ` First few examples:\n`;
143
+ msg += ` ${columns.map((col) => chalk.bold(padCell(col.header, col.width))).join(' | ')}\n`;
144
+ msg += ` ${columns.map((col) => '-'.repeat(col.width)).join('-|-')}\n`;
145
+
146
+ for (const triple of samples) {
147
+ const cells = columns.map((col) =>
148
+ padCell(col.getValue(triple), col.width),
149
+ );
150
+ msg += ` ${cells.join(' | ')}\n`;
151
+ }
152
+ return msg;
153
+ }
154
+ return `Error ${actionMessage}.`;
155
+ }
156
+ }
157
+
158
+ function joinInSentence(items) {
159
+ if (items.length === 0) {
160
+ return '';
161
+ }
162
+ if (items.length === 1) {
163
+ return items[0];
164
+ }
165
+ if (items.length === 2) {
166
+ return `${items[0]} and ${items[1]}`;
167
+ }
168
+ return `${items.slice(0, -1).join(', ')}, and ${items[items.length - 1]}`;
169
+ }
170
+
171
+ function jobGroupDescription(jobs) {
172
+ const actions = new Set();
173
+ const jobActions = {
174
+ 'check-data-type': 'updating types',
175
+ 'remove-data-type': 'updating types',
176
+ index: 'updating indexes',
177
+ 'remove-index': 'updating indexes',
178
+ unique: 'updating uniqueness constraints',
179
+ 'remove-unique': 'updating uniqueness constraints',
180
+ required: 'making attributes required',
181
+ 'remove-required': 'making attributes optional',
182
+ };
183
+ for (const job of jobs) {
184
+ actions.add(jobActions[job.job_type]);
185
+ }
186
+ return joinInSentence([...actions].sort()) || 'updating schema';
187
+ }
188
+
189
+ // TODO: rewrite in effect
190
+ export async function waitForIndexingJobsToFinish(appId, data, authToken) {
191
+ const spinnerDefferedPromise = deferred();
192
+ const spinner = new UI.Spinner({
193
+ promise: spinnerDefferedPromise.promise,
194
+ });
195
+ const spinnerRenderPromise = renderUnwrap(spinner);
196
+
197
+ const groupId = data['group-id'];
198
+ let jobs = data.jobs;
199
+ let waitMs = 20;
200
+ let lastUpdatedAt = new Date(0);
201
+
202
+ const completedIds = new Set();
203
+
204
+ const errorMessages = [];
205
+
206
+ while (true) {
207
+ let stillRunning = false;
208
+ let updated = false;
209
+ let workEstimateTotal = 0;
210
+ let workCompletedTotal = 0;
211
+
212
+ for (const job of jobs) {
213
+ const updatedAt = new Date(job.updated_at);
214
+ if (updatedAt > lastUpdatedAt) {
215
+ updated = true;
216
+ lastUpdatedAt = updatedAt;
217
+ }
218
+ if (job.job_status === 'waiting' || job.job_status === 'processing') {
219
+ stillRunning = true;
220
+ // Default estimate to high value to prevent % from jumping around
221
+ workEstimateTotal += job.work_estimate ?? 50000;
222
+ workCompletedTotal += job.work_completed ?? 0;
223
+ } else {
224
+ if (!completedIds.has(job.id)) {
225
+ completedIds.add(job.id);
226
+ const msg = indexingJobCompletedMessage(job);
227
+ if (msg) {
228
+ if (job.job_status === 'errored') {
229
+ spinner.addMessage(msg);
230
+ errorMessages.push(msg);
231
+ } else {
232
+ spinner.addMessage(msg);
233
+ }
234
+ }
235
+ }
236
+ }
237
+ }
238
+ if (!stillRunning) {
239
+ break;
240
+ }
241
+ if (workEstimateTotal) {
242
+ const percent = Math.floor(
243
+ (workCompletedTotal / workEstimateTotal) * 100,
244
+ );
245
+ spinner.updateText(`${jobGroupDescription(jobs)} ${percent}%`);
246
+ }
247
+ waitMs = updated ? 1 : Math.min(10000, waitMs * 2);
248
+ await sleep(waitMs);
249
+ const res = await fetchJson({
250
+ debugName: 'Check indexing status',
251
+ method: 'GET',
252
+ authToken,
253
+ path: `/dash/apps/${appId}/indexing-jobs/group/${groupId}`,
254
+ errorMessage: 'Failed to check indexing status.',
255
+ command: 'push',
256
+ });
257
+ if (!res.ok) {
258
+ break;
259
+ }
260
+ jobs = res.data.jobs;
261
+ }
262
+
263
+ spinnerDefferedPromise.resolve(null);
264
+
265
+ await spinnerRenderPromise;
266
+
267
+ // Log errors at the end so that they're easier to see.
268
+ if (errorMessages.length) {
269
+ for (const msg of errorMessages) {
270
+ console.log(msg);
271
+ }
272
+ console.log(chalk.red('Some steps failed while updating schema.'));
273
+ process.exit(1);
274
+ }
275
+ }
276
+
277
+ export const resolveRenames = async (created, promptData, extraInfo) => {
278
+ const answer = await renderUnwrap(
279
+ new ResolveRenamePrompt(
280
+ created,
281
+ promptData,
282
+ extraInfo,
283
+ UI.modifiers.piped([
284
+ (out) =>
285
+ boxen(out, {
286
+ dimBorder: true,
287
+ padding: {
288
+ left: 1,
289
+ right: 1,
290
+ },
291
+ }),
292
+ UI.modifiers.vanishOnComplete,
293
+ ]),
294
+ ),
295
+ );
296
+ return answer;
297
+ };
298
+
299
+ /**
300
+ * Fetches JSON data from a specified path using the POST method.
301
+ *
302
+ * @param {Object} options
303
+ * @param {string} options.debugName
304
+ * @param {string} options.errorMessage
305
+ * @param {string} options.path
306
+ * @param {'POST' | 'GET'} [options.method]
307
+ * @param {Object} [options.body=undefined]
308
+ * @param {boolean} [options.noAuth]
309
+ * @param {boolean} [options.noLogError]
310
+ * @param {string} [options.command] - The CLI command being executed (e.g., 'push', 'pull', 'login')
311
+ * @param {string} [options.authToken] - Optional auth token to use instead of reading from config
312
+ * @param {Record<string, string>} [options.headers] - Extra headers to include in the request
313
+ * @returns {Promise<{ ok: boolean; data: any }>}
314
+ */
315
+ async function fetchJson({
316
+ debugName,
317
+ errorMessage,
318
+ path,
319
+ body,
320
+ method,
321
+ noAuth,
322
+ noLogError,
323
+ command,
324
+ authToken: providedAuthToken,
325
+ headers: extraHeaders,
326
+ }) {
327
+ const withAuth = !noAuth;
328
+ const withErrorLogging = !noLogError;
329
+ let authToken = null;
330
+ if (withAuth) {
331
+ authToken =
332
+ providedAuthToken ?? (await readConfigAuthTokenWithErrorLogging());
333
+ if (!authToken) {
334
+ return { ok: false, data: undefined };
335
+ }
336
+ }
337
+ const timeoutMs = 1000 * 60 * 5; // 5 minutes
338
+
339
+ try {
340
+ const res = await fetch(`${instantBackendOrigin}${path}`, {
341
+ method: method ?? 'GET',
342
+ headers: {
343
+ ...(withAuth ? { Authorization: `Bearer ${authToken}` } : {}),
344
+ 'Content-Type': 'application/json',
345
+ 'X-Instant-Source': 'instant-cli',
346
+ 'X-Instant-Version': version,
347
+ ...(command ? { 'X-Instant-Command': command } : {}),
348
+ ...extraHeaders,
349
+ },
350
+ body: body ? JSON.stringify(body) : undefined,
351
+ signal: AbortSignal.timeout(timeoutMs),
352
+ });
353
+
354
+ if (verbose) {
355
+ console.log(debugName, 'response:', res.status, res.statusText);
356
+ }
357
+
358
+ let data;
359
+ try {
360
+ data = await res.json();
361
+ } catch {
362
+ data = null;
363
+ }
364
+ if (verbose && data) {
365
+ console.log(debugName, 'json:', JSON.stringify(data, null, 2));
366
+ }
367
+ if (!res.ok) {
368
+ if (withErrorLogging) {
369
+ error(errorMessage);
370
+ prettyPrintJSONErr(data);
371
+ }
372
+ return { ok: false, data };
373
+ }
374
+
375
+ return { ok: true, data };
376
+ } catch (err) {
377
+ if (withErrorLogging) {
378
+ if (err.name === 'AbortError') {
379
+ error(
380
+ `Timeout: It took more than ${timeoutMs / 60000} minutes to get the result.`,
381
+ );
382
+ } else {
383
+ error(`Error: type: ${err.name}, message: ${err.message}`);
384
+ }
385
+ }
386
+ return { ok: false, data: null };
387
+ }
388
+ }
389
+
390
+ function prettyPrintJSONErr(data) {
391
+ if (data?.message) {
392
+ error(data.message);
393
+ }
394
+ if (Array.isArray(data?.hint?.errors)) {
395
+ for (const err of data.hint.errors) {
396
+ error(`${err.in ? err.in.join('->') + ': ' : ''}${err.message}`);
397
+ }
398
+ }
399
+ if (!data) {
400
+ error('Failed to parse error response');
401
+ }
402
+ }
403
+
404
+ export async function readLocalPermsFile() {
405
+ const readCandidates = getPermsReadCandidates();
406
+ const res = await loadConfig({
407
+ sources: readCandidates,
408
+ merge: false,
409
+ });
410
+ if (!res.config) return;
411
+ const relativePath = path.relative(process.cwd(), res.sources[0]);
412
+ return { path: relativePath, perms: res.config };
413
+ }
414
+
415
+ export async function readLocalSchemaFile() {
416
+ const readCandidates = getSchemaReadCandidates();
417
+ const res = await loadConfig({
418
+ sources: readCandidates,
419
+ merge: false,
420
+ });
421
+ if (!res.config) return;
422
+ const relativePath = path.relative(process.cwd(), res.sources[0]);
423
+ return { path: relativePath, schema: res.config };
424
+ }
425
+
426
+ export async function readInstantConfigFile() {
427
+ return (
428
+ await loadConfig({
429
+ sources: [
430
+ // load from `instant.config.xx`
431
+ {
432
+ files: 'instant.config',
433
+ extensions: ['ts', 'mts', 'cts', 'js', 'mjs', 'cjs', 'json'],
434
+ },
435
+ ],
436
+ // if false, the only the first matched will be loaded
437
+ // if true, all matched will be loaded and deep merged
438
+ merge: false,
439
+ })
440
+ ).config;
441
+ }
442
+
443
+ async function readConfigAuthToken(allowAdminToken = true) {
444
+ const options = program.opts();
445
+ // @ts-expect-error command opts type is unknown
446
+ if (typeof options.token === 'string') {
447
+ // @ts-expect-error command opts type is unknown
448
+ return options.token;
449
+ }
450
+
451
+ if (process.env.INSTANT_CLI_AUTH_TOKEN) {
452
+ return process.env.INSTANT_CLI_AUTH_TOKEN;
453
+ }
454
+
455
+ if (allowAdminToken) {
456
+ const adminTokenNames = Object.values(potentialAdminTokenEnvs);
457
+ for (const envName of adminTokenNames) {
458
+ const token = process.env[envName];
459
+ if (token) {
460
+ return token;
461
+ }
462
+ }
463
+ }
464
+
465
+ const authToken = await readFile(
466
+ getAuthPaths().authConfigFilePath,
467
+ 'utf-8',
468
+ ).catch(() => null);
469
+
470
+ if (authToken) {
471
+ return authToken;
472
+ }
473
+
474
+ return null;
475
+ }
476
+
477
+ export async function readConfigAuthTokenWithErrorLogging() {
478
+ const token = await readConfigAuthToken();
479
+ if (!token) {
480
+ error(
481
+ `Looks like you are not logged in. Please log in with ${chalk.green('`instant-cli login`')}`,
482
+ );
483
+ }
484
+ return token;
485
+ }
486
+
487
+ // utils
488
+
489
+ function sleep(ms) {
490
+ return new Promise((resolve) => setTimeout(resolve, ms));
491
+ }
492
+
493
+ export function countEntities(o) {
494
+ return Object.keys(o).length;
495
+ }
package/src/program.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { Command } from '@commander-js/extra-typings';
2
+
3
+ export const program = new Command();
@@ -1,12 +1,11 @@
1
- import {
2
- convertTxSteps,
1
+ import { convertTxSteps } from '@instantdb/platform';
2
+ import type {
3
3
  MigrationTx,
4
4
  MigrationTxSpecific,
5
5
  MigrationTxTypes,
6
6
  } from '@instantdb/platform';
7
7
  import chalk from 'chalk';
8
8
  import stripAnsi from 'strip-ansi';
9
- import { promptOk } from './util/promptOk.js';
10
9
 
11
10
  // Hack to prevent using @instantdb/core as a dependency for cli
12
11
  type InstantDBAttr = Parameters<typeof convertTxSteps>[1][0];
@@ -98,7 +97,7 @@ type AddOrDeleteAttr =
98
97
  | MigrationTxSpecific<'add-attr'>
99
98
  | MigrationTxSpecific<'delete-attr'>;
100
99
 
101
- type SuperMigrationTx =
100
+ export type SuperMigrationTx =
102
101
  | MigrationTx
103
102
  | { type: 'create-namespace'; namespace: string; innerSteps: MigrationTx[] }
104
103
  | { type: 'delete-namespace'; namespace: string; innerSteps: MigrationTx[] };
@@ -169,19 +168,6 @@ export const groupSteps = (steps: MigrationTx[]): SuperMigrationTx[] => {
169
168
  return [...collapsed, ...otherSteps];
170
169
  };
171
170
 
172
- export const confirmImportantSteps = async (planSteps: SuperMigrationTx[]) => {
173
- for (const step of planSteps) {
174
- if (step.type === 'delete-namespace') {
175
- const ok = await promptOk({
176
- promptText: `Are you sure you want to delete namespace ${step.namespace}?`,
177
- });
178
- if (!ok) {
179
- throw new CancelSchemaError(`Deletion of namespace ${step.namespace}`);
180
- }
181
- }
182
- }
183
- };
184
-
185
171
  const createDotName = (step: MigrationTx) => {
186
172
  return `${step.identifier.namespace}.${step.identifier.attrName}`;
187
173
  };
@@ -328,7 +314,9 @@ export const renderSchemaPlan = (
328
314
  break;
329
315
 
330
316
  default:
331
- const unknownStep: never = step;
317
+ throw new Error(
318
+ `Unknown migration step: ${(step as { type: string }).type}`,
319
+ );
332
320
  }
333
321
  }
334
322
 
package/src/ui/index.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import chalk from 'chalk';
2
2
  import boxen from 'boxen';
3
3
  import stringWidth from 'string-width';
4
- import { AnyKey, ModifyOutputFn, Prompt, SelectState } from './lib.js';
4
+ import { Prompt, SelectState } from './lib.ts';
5
+ import type { AnyKey, ModifyOutputFn } from './lib.ts';
5
6
 
6
- export { render, renderUnwrap, setRawModeWindowsFriendly } from './lib.js';
7
+ export { render, renderUnwrap, setRawModeWindowsFriendly } from './lib.ts';
7
8
 
8
9
  export namespace UI {
9
10
  type Status = 'idle' | 'submitted' | 'aborted';
@@ -864,7 +865,7 @@ ${inputDisplay}`;
864
865
  }
865
866
  }
866
867
 
867
- interface AppSelectorApi {
868
+ export interface AppSelectorApi {
868
869
  getDash: () => { apps: App[]; orgs: Org[] };
869
870
  createEphemeralApp: (title: string) => Promise<{
870
871
  appId: string;
@@ -187,8 +187,7 @@ export function getSchemaPathToWrite(existingPath?: string): string {
187
187
  return 'instant.schema.ts';
188
188
  }
189
189
 
190
- export function getPermsPathToWrite(existingPath?: string): string {
191
- if (existingPath) return existingPath;
190
+ export function getPermsPathToWrite(): string {
192
191
  if (process.env.INSTANT_PERMS_FILE_PATH) {
193
192
  return process.env.INSTANT_PERMS_FILE_PATH;
194
193
  }
package/src/util/fs.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { PathLike } from 'fs';
1
+ import type { PathLike } from 'fs';
2
2
  import { readFile, stat } from 'fs/promises';
3
3
 
4
4
  export async function pathExists(p: PathLike): Promise<boolean> {
@@ -2,6 +2,7 @@ import envPaths from 'env-paths';
2
2
  import { join } from 'path';
3
3
 
4
4
  const dev = Boolean(process.env.INSTANT_CLI_DEV);
5
+
5
6
  export function getAuthPaths() {
6
7
  const key = `instantdb-${dev ? 'dev' : 'prod'}`;
7
8
  const { config: appConfigDirPath } = envPaths(key);
@@ -1,4 +1,4 @@
1
- export function isHeadlessEnvironment(opts) {
1
+ export function isHeadlessEnvironment(opts?: { headless?: boolean }) {
2
2
  const noBrowserMode = Boolean(
3
3
  process.env.INSTANT_CLI_NO_BROWSER || process.env.CI || opts?.headless,
4
4
  );
@@ -1,11 +1,8 @@
1
- import {
2
- loadConfig as _loadConfig,
3
- LoadConfigOptions,
4
- LoadConfigResult,
5
- } from 'unconfig';
1
+ import { loadConfig as _loadConfig } from 'unconfig';
2
+ import type { LoadConfigOptions, LoadConfigResult } from 'unconfig';
6
3
  import { createRequire } from 'module';
7
4
  import path from 'path';
8
- import { findProjectDir } from './projectDir.js';
5
+ import { findProjectDir } from './projectDir.ts';
9
6
 
10
7
  /**
11
8
  * Resolve @instantdb packages from CLI's dependency tree.