atmn 0.0.4 → 0.0.6

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