atmn 0.0.4 → 0.0.5
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/dist/cli.cjs +586 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +564 -51
- package/package.json +3 -2
- package/dist/app.d.ts +0 -6
- package/dist/app.js +0 -7
- package/dist/commands/auth.d.ts +0 -1
- package/dist/commands/auth.js +0 -63
- package/dist/commands/init.d.ts +0 -3
- package/dist/commands/init.js +0 -26
- package/dist/commands/pull.d.ts +0 -3
- package/dist/commands/pull.js +0 -28
- package/dist/commands/push.d.ts +0 -5
- package/dist/commands/push.js +0 -47
- package/dist/constants.d.ts +0 -3
- package/dist/constants.js +0 -55
- package/dist/core/api.d.ts +0 -27
- package/dist/core/api.js +0 -75
- package/dist/core/auth.d.ts +0 -1
- package/dist/core/auth.js +0 -8
- package/dist/core/builders/features.d.ts +0 -2
- package/dist/core/builders/features.js +0 -10
- package/dist/core/builders/products.d.ts +0 -7
- package/dist/core/builders/products.js +0 -71
- package/dist/core/config.d.ts +0 -2
- package/dist/core/config.js +0 -25
- package/dist/core/pull.d.ts +0 -17
- package/dist/core/pull.js +0 -22
- package/dist/core/push.d.ts +0 -7
- package/dist/core/push.js +0 -67
- package/dist/core/utils.d.ts +0 -3
- package/dist/core/utils.js +0 -26
package/dist/cli.js
CHANGED
|
@@ -1,58 +1,571 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { program } from 'commander';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
3
|
+
import chalk6 from 'chalk';
|
|
4
|
+
import axios from 'axios';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path, { resolve } from 'path';
|
|
7
|
+
import createJiti from 'jiti';
|
|
8
|
+
import { pathToFileURL } from 'url';
|
|
8
9
|
import open from 'open';
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
10
|
+
import { confirm, input, password } from '@inquirer/prompts';
|
|
11
|
+
|
|
12
|
+
// source/constants.ts
|
|
13
|
+
var FRONTEND_URL = "http://app.useautumn.com";
|
|
14
|
+
var BACKEND_URL = "https://api.useautumn.com";
|
|
15
|
+
var DEFAULT_CONFIG = `import {
|
|
16
|
+
feature,
|
|
17
|
+
product,
|
|
18
|
+
priceItem,
|
|
19
|
+
featureItem,
|
|
20
|
+
pricedFeatureItem,
|
|
21
|
+
} from 'autumn-js/compose';
|
|
22
|
+
|
|
23
|
+
const seats = feature({
|
|
24
|
+
id: 'seats',
|
|
25
|
+
name: 'Seats',
|
|
26
|
+
type: 'continuous_use',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const messages = feature({
|
|
30
|
+
id: 'messages',
|
|
31
|
+
name: 'Messages',
|
|
32
|
+
type: 'single_use',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const pro = product({
|
|
36
|
+
id: 'pro',
|
|
37
|
+
name: 'Pro',
|
|
38
|
+
items: [
|
|
39
|
+
// 500 messages per month
|
|
40
|
+
featureItem({
|
|
41
|
+
feature_id: messages.id,
|
|
42
|
+
included_usage: 500,
|
|
43
|
+
interval: 'month',
|
|
44
|
+
}),
|
|
45
|
+
|
|
46
|
+
// $10 per seat per month
|
|
47
|
+
pricedFeatureItem({
|
|
48
|
+
feature_id: seats.id,
|
|
49
|
+
price: 10,
|
|
50
|
+
interval: 'month',
|
|
51
|
+
}),
|
|
52
|
+
|
|
53
|
+
// $50 / month
|
|
54
|
+
priceItem({
|
|
55
|
+
price: 50,
|
|
56
|
+
interval: 'month',
|
|
57
|
+
}),
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export default {
|
|
62
|
+
features: [seats, messages],
|
|
63
|
+
products: [pro],
|
|
64
|
+
};
|
|
65
|
+
`;
|
|
66
|
+
function snakeCaseToCamelCase(value) {
|
|
67
|
+
return value.replace(/_([a-z])/g, (match, letter) => letter.toUpperCase());
|
|
68
|
+
}
|
|
69
|
+
function storeToEnv(prodKey, sandboxKey) {
|
|
70
|
+
const envPath = `${process.cwd()}/.env`;
|
|
71
|
+
const envVars = `# AUTUMN_SECRET_KEY=${prodKey}
|
|
72
|
+
AUTUMN_SECRET_KEY=${sandboxKey}
|
|
73
|
+
`;
|
|
74
|
+
if (fs.existsSync(envPath)) {
|
|
75
|
+
fs.appendFileSync(envPath, envVars);
|
|
76
|
+
console.log(chalk6.green(".env file found. Appended keys."));
|
|
77
|
+
} else {
|
|
78
|
+
fs.writeFileSync(envPath, envVars);
|
|
79
|
+
console.log(chalk6.green(".env file not found. Created new .env file and wrote keys."));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function readFromEnv() {
|
|
83
|
+
const envPath = `${process.cwd()}/.env`;
|
|
84
|
+
if (!fs.existsSync(envPath)) {
|
|
85
|
+
return void 0;
|
|
86
|
+
}
|
|
87
|
+
const envContent = fs.readFileSync(envPath, "utf-8");
|
|
88
|
+
const match = envContent.match(/^AUTUMN_SECRET_KEY=(.*)$/m);
|
|
89
|
+
return match ? match[1] : void 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// source/core/api.ts
|
|
93
|
+
var INTERNAL_BASE = BACKEND_URL;
|
|
94
|
+
var EXTERNAL_BASE = `${BACKEND_URL}/v1`;
|
|
95
|
+
async function request({
|
|
96
|
+
method,
|
|
97
|
+
base,
|
|
98
|
+
path: path2,
|
|
99
|
+
data,
|
|
100
|
+
headers,
|
|
101
|
+
customAuth,
|
|
102
|
+
throwOnError = true
|
|
103
|
+
}) {
|
|
104
|
+
const apiKey = readFromEnv();
|
|
105
|
+
try {
|
|
106
|
+
const response = await axios.request({
|
|
107
|
+
method,
|
|
108
|
+
url: `${base}${path2}`,
|
|
109
|
+
data,
|
|
110
|
+
headers: {
|
|
111
|
+
"Content-Type": "application/json",
|
|
112
|
+
...headers,
|
|
113
|
+
Authorization: customAuth || `Bearer ${apiKey}`
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
return response.data;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (throwOnError) {
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
console.error(
|
|
122
|
+
chalk6.red("Error occured when making API request:"),
|
|
123
|
+
chalk6.red(error.response.data.message || error.response.data.error)
|
|
124
|
+
);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function internalRequest({
|
|
129
|
+
method,
|
|
130
|
+
path: path2,
|
|
131
|
+
data,
|
|
132
|
+
headers,
|
|
133
|
+
customAuth
|
|
134
|
+
}) {
|
|
135
|
+
return await request({
|
|
136
|
+
method,
|
|
137
|
+
base: INTERNAL_BASE,
|
|
138
|
+
path: path2,
|
|
139
|
+
data,
|
|
140
|
+
headers,
|
|
141
|
+
customAuth
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
async function externalRequest({
|
|
145
|
+
method,
|
|
146
|
+
path: path2,
|
|
147
|
+
data,
|
|
148
|
+
headers,
|
|
149
|
+
customAuth,
|
|
150
|
+
throwOnError = false
|
|
151
|
+
}) {
|
|
152
|
+
return await request({
|
|
153
|
+
method,
|
|
154
|
+
base: EXTERNAL_BASE,
|
|
155
|
+
path: path2,
|
|
156
|
+
data,
|
|
157
|
+
headers,
|
|
158
|
+
customAuth,
|
|
159
|
+
throwOnError
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async function deleteFeature(id) {
|
|
163
|
+
return await externalRequest({
|
|
164
|
+
method: "DELETE",
|
|
165
|
+
path: `/features/${id}`
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
async function deleteProduct(id) {
|
|
169
|
+
return await externalRequest({
|
|
170
|
+
method: "DELETE",
|
|
171
|
+
path: `/products/${id}`
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
async function updateCLIStripeKeys(stripeTestKey, stripeLiveKey, stripeFlowAuthKey) {
|
|
175
|
+
return await internalRequest({
|
|
176
|
+
method: "POST",
|
|
177
|
+
path: "/dev/cli/stripe",
|
|
178
|
+
data: {
|
|
179
|
+
stripeTestKey,
|
|
180
|
+
stripeLiveKey,
|
|
181
|
+
successUrl: "https://useautumn.com",
|
|
182
|
+
defaultCurrency: "usd"
|
|
183
|
+
},
|
|
184
|
+
customAuth: stripeFlowAuthKey
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// source/core/pull.ts
|
|
189
|
+
async function getAllProducts() {
|
|
190
|
+
const { list } = await externalRequest({
|
|
191
|
+
method: "GET",
|
|
192
|
+
path: "/products"
|
|
193
|
+
});
|
|
194
|
+
return list.map((product) => product);
|
|
195
|
+
}
|
|
196
|
+
async function getFeatures() {
|
|
197
|
+
const { list } = await externalRequest({
|
|
198
|
+
method: "GET",
|
|
199
|
+
path: "/features"
|
|
200
|
+
});
|
|
201
|
+
return list.map((feature) => feature);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// source/core/builders/products.ts
|
|
205
|
+
var ItemBuilders = {
|
|
206
|
+
priced_feature: pricedFeatureItemBuilder,
|
|
207
|
+
feature: featureItemBuilder,
|
|
208
|
+
price: priceItemBuilder
|
|
209
|
+
};
|
|
210
|
+
function importBuilder() {
|
|
211
|
+
return `
|
|
212
|
+
import {
|
|
213
|
+
feature,
|
|
214
|
+
product,
|
|
215
|
+
featureItem,
|
|
216
|
+
pricedFeatureItem,
|
|
217
|
+
priceItem,
|
|
218
|
+
} from 'autumn-js/compose';
|
|
219
|
+
`;
|
|
220
|
+
}
|
|
221
|
+
function exportBuilder(productIds, featureIds) {
|
|
222
|
+
const snippet = `
|
|
223
|
+
export default {
|
|
224
|
+
products: [${productIds.map((id) => `${id}Plan`).join(", ")}],
|
|
225
|
+
features: [${featureIds.map((id) => `${id}`).join(", ")}]
|
|
226
|
+
}
|
|
227
|
+
`;
|
|
228
|
+
return snippet;
|
|
229
|
+
}
|
|
230
|
+
function productBuilder(product) {
|
|
231
|
+
const snippet = `
|
|
232
|
+
export const ${product.id}Plan = product({
|
|
233
|
+
id: '${product.id}',
|
|
234
|
+
name: '${product.name}',
|
|
235
|
+
items: [${product.items.map((item) => `${ItemBuilders[item.type](item)}`).join(" ")} ]
|
|
236
|
+
})
|
|
237
|
+
`;
|
|
238
|
+
return snippet;
|
|
239
|
+
}
|
|
240
|
+
function pricedFeatureItemBuilder(item) {
|
|
241
|
+
const intervalLine = item.interval == null ? "" : `
|
|
242
|
+
interval: '${item.interval}',`;
|
|
243
|
+
const snippet = `
|
|
244
|
+
pricedFeatureItem({
|
|
245
|
+
feature_id: ${snakeCaseToCamelCase(item.feature_id)}.id,
|
|
246
|
+
price: ${item.price},${intervalLine}
|
|
247
|
+
included_usage: ${item.included_usage},
|
|
248
|
+
billing_units: ${item.billing_units},
|
|
249
|
+
usage_model: '${item.usage_model}',
|
|
250
|
+
}),
|
|
251
|
+
`;
|
|
252
|
+
return snippet;
|
|
253
|
+
}
|
|
254
|
+
function featureItemBuilder(item) {
|
|
255
|
+
const intervalLine = item.interval == null ? "" : `
|
|
256
|
+
interval: '${item.interval}',`;
|
|
257
|
+
const snippet = `
|
|
258
|
+
featureItem({
|
|
259
|
+
feature_id: ${snakeCaseToCamelCase(item.feature_id)}.id,
|
|
260
|
+
included_usage: ${item.included_usage},${intervalLine}
|
|
261
|
+
}),
|
|
262
|
+
`;
|
|
263
|
+
return snippet;
|
|
264
|
+
}
|
|
265
|
+
function priceItemBuilder(item) {
|
|
266
|
+
const intervalLine = item.interval == null ? "" : `
|
|
267
|
+
interval: '${item.interval}',`;
|
|
268
|
+
const snippet = `
|
|
269
|
+
priceItem({
|
|
270
|
+
price: ${item.price},${intervalLine}
|
|
271
|
+
}),
|
|
272
|
+
`;
|
|
273
|
+
return snippet;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// source/core/builders/features.ts
|
|
277
|
+
function featureBuilder(feature) {
|
|
278
|
+
const snippet = `
|
|
279
|
+
export const ${snakeCaseToCamelCase(feature.id)} = feature({
|
|
280
|
+
id: '${feature.id}',
|
|
281
|
+
name: '${feature.name}',
|
|
282
|
+
type: '${feature.type}',
|
|
283
|
+
})`;
|
|
284
|
+
return snippet;
|
|
285
|
+
}
|
|
286
|
+
async function loadAutumnConfigFile() {
|
|
287
|
+
const configPath = path.join(process.cwd(), "autumn.config.ts");
|
|
288
|
+
const absolutePath = resolve(configPath);
|
|
289
|
+
const fileUrl = pathToFileURL(absolutePath).href;
|
|
290
|
+
const jiti = createJiti(import.meta.url);
|
|
291
|
+
const mod = await jiti.import(fileUrl);
|
|
292
|
+
const def = mod.default || mod;
|
|
293
|
+
if (!def.products || !Array.isArray(def.products)) {
|
|
294
|
+
throw new Error("You must export a products field that is an array of products.");
|
|
295
|
+
}
|
|
296
|
+
if (!def.features || !Array.isArray(def.features)) {
|
|
297
|
+
throw new Error("You must export a features field that is an array of products.");
|
|
298
|
+
}
|
|
299
|
+
return def;
|
|
300
|
+
}
|
|
301
|
+
function writeConfig(config) {
|
|
302
|
+
const configPath = path.join(process.cwd(), "autumn.config.ts");
|
|
303
|
+
fs.writeFileSync(
|
|
304
|
+
configPath,
|
|
305
|
+
config
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// source/commands/pull.ts
|
|
310
|
+
async function Pull({ config }) {
|
|
311
|
+
console.log(chalk6.green("Pulling products and features from Autumn..."));
|
|
312
|
+
const products = await getAllProducts();
|
|
313
|
+
const features = await getFeatures();
|
|
314
|
+
const productSnippets = products.map((product) => productBuilder(product));
|
|
315
|
+
const featureSnippets = features.map((feature) => featureBuilder(feature));
|
|
316
|
+
const autumnConfig = `
|
|
317
|
+
${importBuilder()}
|
|
318
|
+
|
|
319
|
+
// Features
|
|
320
|
+
${featureSnippets.join("\n")}
|
|
321
|
+
|
|
322
|
+
// Products
|
|
323
|
+
${productSnippets.join("\n")}
|
|
324
|
+
|
|
325
|
+
// Remember to update this when you make changes!
|
|
326
|
+
${exportBuilder(products.map((product) => product.id), features.map((feature) => snakeCaseToCamelCase(feature.id)))}
|
|
327
|
+
`;
|
|
328
|
+
writeConfig(autumnConfig);
|
|
329
|
+
console.log(chalk6.green("Success! Config has been updated."));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// source/core/auth.ts
|
|
333
|
+
async function getOTP(otp) {
|
|
334
|
+
const response = await internalRequest({
|
|
335
|
+
method: "GET",
|
|
336
|
+
path: `/dev/otp/${otp}`
|
|
337
|
+
});
|
|
338
|
+
return response;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// source/commands/auth.ts
|
|
342
|
+
var passwordTheme = {
|
|
343
|
+
style: {
|
|
344
|
+
answer: (text) => {
|
|
345
|
+
return chalk6.magenta("*".repeat(text.length));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
var inputTheme = {
|
|
350
|
+
style: {
|
|
351
|
+
answer: (text) => {
|
|
352
|
+
return chalk6.magenta(text);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
async function AuthCommand() {
|
|
357
|
+
if (readFromEnv()) {
|
|
358
|
+
let shouldReauth = await confirm({
|
|
359
|
+
message: "You are already authenticated. Would you like to re-authenticate?",
|
|
360
|
+
theme: inputTheme
|
|
361
|
+
});
|
|
362
|
+
if (!shouldReauth) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
open(`${FRONTEND_URL}/dev/cli`);
|
|
367
|
+
const otp = await input({
|
|
368
|
+
message: "Enter OTP:",
|
|
369
|
+
theme: inputTheme
|
|
370
|
+
});
|
|
371
|
+
const keyInfo = await getOTP(otp);
|
|
372
|
+
if (!keyInfo.stripe_connected) {
|
|
373
|
+
let connectStripe = await confirm({
|
|
374
|
+
message: "It seems like your organization doesn't have any Stripe keys connected. Would you like to connect them now?",
|
|
375
|
+
theme: inputTheme
|
|
376
|
+
});
|
|
377
|
+
if (connectStripe) {
|
|
378
|
+
let stripeTestKey = await password({
|
|
379
|
+
message: "Enter Stripe Test Secret Key:",
|
|
380
|
+
mask: "*",
|
|
381
|
+
theme: passwordTheme
|
|
382
|
+
});
|
|
383
|
+
await updateCLIStripeKeys(
|
|
384
|
+
stripeTestKey,
|
|
385
|
+
stripeTestKey,
|
|
386
|
+
keyInfo.stripeFlowAuthKey
|
|
387
|
+
);
|
|
388
|
+
} else {
|
|
389
|
+
console.log(
|
|
390
|
+
chalk6.yellow(
|
|
391
|
+
"Okay, no worries. Go to the Autumn dashboard when you're ready!"
|
|
392
|
+
)
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
storeToEnv(keyInfo.prodKey, keyInfo.sandboxKey);
|
|
397
|
+
console.log(
|
|
398
|
+
chalk6.green(
|
|
399
|
+
"Success! Keys have been stored locally. You may now use the CLI."
|
|
400
|
+
)
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// source/commands/init.ts
|
|
405
|
+
async function Init({ config }) {
|
|
406
|
+
let apiKey = readFromEnv();
|
|
407
|
+
if (apiKey) {
|
|
408
|
+
console.log(chalk6.green("API key found. Pulling latest config..."));
|
|
409
|
+
await Pull({ config });
|
|
410
|
+
console.log(chalk6.green("Project initialized and config pulled successfully!"));
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
console.log(chalk6.yellow("No API key found. Running authentication..."));
|
|
414
|
+
await AuthCommand();
|
|
415
|
+
apiKey = readFromEnv();
|
|
416
|
+
if (apiKey) {
|
|
29
417
|
await Pull({ config });
|
|
418
|
+
console.log(chalk6.green("Project initialized! You are now authenticated and config has been pulled."));
|
|
419
|
+
} else {
|
|
420
|
+
console.log(chalk6.red("Authentication did not yield an API key. Please check your setup."));
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// source/core/push.ts
|
|
425
|
+
async function checkForDeletables(currentFeatures, currentProducts) {
|
|
426
|
+
const features = await getFeatures();
|
|
427
|
+
const featureIds = features.map((feature) => feature.id);
|
|
428
|
+
const currentFeatureIds = currentFeatures.map((feature) => feature.id);
|
|
429
|
+
const featuresToDelete = featureIds.filter(
|
|
430
|
+
(featureId) => !currentFeatureIds.includes(featureId)
|
|
431
|
+
);
|
|
432
|
+
const products = await getAllProducts();
|
|
433
|
+
const productIds = products.map((product) => product.id);
|
|
434
|
+
const currentProductIds = currentProducts.map((product) => product.id);
|
|
435
|
+
const productsToDelete = productIds.filter(
|
|
436
|
+
(productId) => !currentProductIds.includes(productId)
|
|
437
|
+
);
|
|
438
|
+
return { featuresToDelete, productsToDelete };
|
|
439
|
+
}
|
|
440
|
+
async function upsertFeature(feature) {
|
|
441
|
+
try {
|
|
442
|
+
const response = await externalRequest({
|
|
443
|
+
method: "POST",
|
|
444
|
+
path: `/features`,
|
|
445
|
+
data: feature,
|
|
446
|
+
throwOnError: true
|
|
447
|
+
// data: {
|
|
448
|
+
// ...feature,
|
|
449
|
+
// config: {
|
|
450
|
+
// filters: [{property: '', operator: '', value: []}],
|
|
451
|
+
// usage_type: 'single_use',
|
|
452
|
+
// },
|
|
453
|
+
// },
|
|
454
|
+
});
|
|
455
|
+
return response.data;
|
|
456
|
+
} catch (error) {
|
|
457
|
+
const response = await externalRequest({
|
|
458
|
+
method: "POST",
|
|
459
|
+
path: `/features/${feature.id}`,
|
|
460
|
+
data: feature
|
|
461
|
+
// data: {
|
|
462
|
+
// ...feature,
|
|
463
|
+
// config: {
|
|
464
|
+
// filters: [{property: '', operator: '', value: []}],
|
|
465
|
+
// usage_type: 'single_use',
|
|
466
|
+
// },
|
|
467
|
+
// },
|
|
468
|
+
});
|
|
469
|
+
return response.data;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
async function upsertProduct(product) {
|
|
473
|
+
try {
|
|
474
|
+
const response = await externalRequest({
|
|
475
|
+
method: "POST",
|
|
476
|
+
path: `/products`,
|
|
477
|
+
data: product,
|
|
478
|
+
throwOnError: true
|
|
479
|
+
});
|
|
480
|
+
return response.data;
|
|
481
|
+
} catch (error) {
|
|
482
|
+
const response = await externalRequest({
|
|
483
|
+
method: "POST",
|
|
484
|
+
path: `/products/${product.id}`,
|
|
485
|
+
data: product
|
|
486
|
+
});
|
|
487
|
+
return response.data;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// source/commands/push.ts
|
|
492
|
+
async function Push({
|
|
493
|
+
config,
|
|
494
|
+
yes,
|
|
495
|
+
prod
|
|
496
|
+
}) {
|
|
497
|
+
let { features, products } = config;
|
|
498
|
+
let { featuresToDelete, productsToDelete } = await checkForDeletables(
|
|
499
|
+
features,
|
|
500
|
+
products
|
|
501
|
+
);
|
|
502
|
+
for (let productId of productsToDelete) {
|
|
503
|
+
let shouldDelete = yes || await confirm({
|
|
504
|
+
message: `Delete product [${productId}]?`
|
|
505
|
+
});
|
|
506
|
+
if (shouldDelete) {
|
|
507
|
+
await deleteProduct(productId);
|
|
508
|
+
console.log(chalk6.green(`Product [${productId}] deleted successfully!`));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
for (let feature of features) {
|
|
512
|
+
console.log(chalk6.green(`Pushing feature [${feature.id}]`));
|
|
513
|
+
await upsertFeature(feature);
|
|
514
|
+
console.log(chalk6.green(`Pushed feature [${feature.id}]`));
|
|
515
|
+
}
|
|
516
|
+
for (let product of products) {
|
|
517
|
+
console.log(chalk6.green(`Pushing product [${product.id}]`));
|
|
518
|
+
await upsertProduct(product);
|
|
519
|
+
console.log(chalk6.green(`Pushed product [${product.id}]`));
|
|
520
|
+
}
|
|
521
|
+
for (let featureId of featuresToDelete) {
|
|
522
|
+
let shouldDelete = yes || await confirm({
|
|
523
|
+
message: `Delete feature [${featureId}]?`
|
|
524
|
+
});
|
|
525
|
+
if (shouldDelete) {
|
|
526
|
+
await deleteFeature(featureId);
|
|
527
|
+
console.log(chalk6.green(`Feature [${featureId}] deleted successfully!`));
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
const env = prod ? "prod" : "sandbox";
|
|
531
|
+
console.log(
|
|
532
|
+
chalk6.magentaBright(`Success! Changes have been pushed to ${env}.`)
|
|
533
|
+
);
|
|
534
|
+
if (prod) {
|
|
535
|
+
console.log(
|
|
536
|
+
chalk6.magentaBright(
|
|
537
|
+
`You can view the products at ${FRONTEND_URL}/products`
|
|
538
|
+
)
|
|
539
|
+
);
|
|
540
|
+
} else {
|
|
541
|
+
console.log(
|
|
542
|
+
chalk6.magentaBright(
|
|
543
|
+
`You can view the products at ${FRONTEND_URL}/sandbox/products`
|
|
544
|
+
)
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
var VERSION = "1.0.0b";
|
|
549
|
+
program.command("push").description("Push changes to Autumn").option("-p, --prod", "Push to production").option("-y, --yes", "Confirm all deletions").action(async (options) => {
|
|
550
|
+
const config = await loadAutumnConfigFile();
|
|
551
|
+
await Push({ config, yes: options.yes, prod: options.prod });
|
|
552
|
+
});
|
|
553
|
+
program.command("pull").description("Pull changes from Autumn").option("-p, --prod", "Pull from production").action(async () => {
|
|
554
|
+
const config = await loadAutumnConfigFile();
|
|
555
|
+
await Pull({ config });
|
|
556
|
+
});
|
|
557
|
+
program.command("init").description("Initialize an Autumn project.").action(async () => {
|
|
558
|
+
writeConfig(DEFAULT_CONFIG);
|
|
559
|
+
const config = await loadAutumnConfigFile();
|
|
560
|
+
await Init({ config });
|
|
561
|
+
});
|
|
562
|
+
program.command("login").description("Authenticate with Autumn").option("-p, --prod", "Authenticate with production").action(async () => {
|
|
563
|
+
await AuthCommand();
|
|
564
|
+
});
|
|
565
|
+
program.command("dashboard").description("Open the Autumn dashboard in your browser").action(() => {
|
|
566
|
+
open(`${FRONTEND_URL}`);
|
|
30
567
|
});
|
|
31
|
-
program
|
|
32
|
-
|
|
33
|
-
.description('Initialize an Autumn project.')
|
|
34
|
-
.action(async () => {
|
|
35
|
-
writeConfig(DEFAULT_CONFIG); // just write an empty config to make the config file.
|
|
36
|
-
const config = await loadAutumnConfigFile();
|
|
37
|
-
await Init({ config });
|
|
38
|
-
});
|
|
39
|
-
program
|
|
40
|
-
.command('login')
|
|
41
|
-
.description('Authenticate with Autumn')
|
|
42
|
-
.option('-p, --prod', 'Authenticate with production')
|
|
43
|
-
.action(async () => {
|
|
44
|
-
await AuthCommand();
|
|
45
|
-
});
|
|
46
|
-
program
|
|
47
|
-
.command('dashboard')
|
|
48
|
-
.description('Open the Autumn dashboard in your browser')
|
|
49
|
-
.action(() => {
|
|
50
|
-
open(`${FRONTEND_URL}`);
|
|
51
|
-
});
|
|
52
|
-
program
|
|
53
|
-
.command('version')
|
|
54
|
-
.description('Show the version of Autumn')
|
|
55
|
-
.action(() => {
|
|
56
|
-
console.log(chalk.green(`Autumn v${VERSION}`));
|
|
568
|
+
program.command("version").description("Show the version of Autumn").action(() => {
|
|
569
|
+
console.log(chalk6.green(`Autumn v${VERSION}`));
|
|
57
570
|
});
|
|
58
571
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atmn",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": "dist/cli.js",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"node": ">=16"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"build": "
|
|
12
|
+
"build": "tsup",
|
|
13
13
|
"dev": "tsc --watch",
|
|
14
14
|
"test2": "prettier --check . && xo && ava",
|
|
15
15
|
"test": "node ./dist/cli.js"
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"ink-testing-library": "^3.0.0",
|
|
43
43
|
"prettier": "^2.8.7",
|
|
44
44
|
"ts-node": "^10.9.1",
|
|
45
|
+
"tsup": "^8.4.0",
|
|
45
46
|
"typescript": "^5.0.3",
|
|
46
47
|
"xo": "^0.53.1"
|
|
47
48
|
},
|
package/dist/app.d.ts
DELETED
package/dist/app.js
DELETED
package/dist/commands/auth.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function AuthCommand(): Promise<void>;
|