atmn 0.0.1 → 0.0.2

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/app.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ type Props = {
3
+ name: string | undefined;
4
+ };
5
+ export default function App({ name }: Props): React.JSX.Element;
6
+ export {};
package/dist/app.js ADDED
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import { Text } from 'ink';
3
+ export default function App({ name = 'Stranger' }) {
4
+ return (React.createElement(Text, null,
5
+ "Hello, ",
6
+ React.createElement(Text, { color: "green" }, name)));
7
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+ import { program } from 'commander';
3
+ import Init from './commands/init.js';
4
+ import { loadAutumnConfigFile } from './core/config.js';
5
+ import Push from './commands/push.js';
6
+ import Pull from './commands/pull.js';
7
+ import AuthCommand from './commands/auth.js';
8
+ import open from 'open';
9
+ import chalk from 'chalk';
10
+ import { writeConfig } from './core/config.js';
11
+ import { FRONTEND_URL } from './constants.js';
12
+ import { DEFAULT_CONFIG } from './constants.js';
13
+ const VERSION = '1.0.0b';
14
+ program
15
+ .command('push')
16
+ .description('Push changes to the remote repository')
17
+ .option('-p, --prod', 'Push to production')
18
+ .option('-y, --yes', 'Confirm all deletions')
19
+ .action(async (options) => {
20
+ const config = await loadAutumnConfigFile();
21
+ await Push({ config, yes: options.yes, prod: options.prod });
22
+ });
23
+ program
24
+ .command('pull')
25
+ .description('Pull changes from Autumn')
26
+ .option('-p, --prod', 'Pull from production')
27
+ .action(async () => {
28
+ const config = await loadAutumnConfigFile();
29
+ await Pull({ config });
30
+ });
31
+ program
32
+ .command('init')
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}`));
57
+ });
58
+ program.parse();
@@ -0,0 +1 @@
1
+ export default function AuthCommand(): Promise<void>;
@@ -0,0 +1,63 @@
1
+ import open from 'open';
2
+ import chalk from 'chalk';
3
+ import { input, password, confirm } from '@inquirer/prompts';
4
+ import { storeToEnv, readFromEnv } from '../core/utils.js';
5
+ import { getOTP } from '../core/auth.js';
6
+ import { updateCLIStripeKeys } from '../core/api.js';
7
+ import { FRONTEND_URL } from '../constants.js';
8
+ const passwordTheme = {
9
+ style: {
10
+ answer: (text) => {
11
+ return chalk.magenta('*'.repeat(text.length));
12
+ },
13
+ },
14
+ };
15
+ const inputTheme = {
16
+ style: {
17
+ answer: (text) => {
18
+ return chalk.magenta(text);
19
+ },
20
+ },
21
+ };
22
+ export default async function AuthCommand() {
23
+ if (readFromEnv()) {
24
+ let shouldReauth = await confirm({
25
+ message: 'You are already authenticated. Would you like to re-authenticate?',
26
+ theme: inputTheme,
27
+ });
28
+ if (!shouldReauth) {
29
+ return;
30
+ }
31
+ }
32
+ open(`${FRONTEND_URL}/dev/cli`);
33
+ const otp = await input({
34
+ message: 'Enter OTP:',
35
+ theme: inputTheme,
36
+ });
37
+ const keyInfo = await getOTP(otp);
38
+ if (!keyInfo.stripe_connected) {
39
+ let connectStripe = await confirm({
40
+ message: "It seems like your organization doesn't have any Stripe keys connected. Would you like to connect them now?",
41
+ theme: inputTheme,
42
+ });
43
+ if (connectStripe) {
44
+ // Ask for stripe Keys
45
+ let stripeTestKey = await password({
46
+ message: 'Enter Stripe Test Secret Key:',
47
+ mask: '*',
48
+ theme: passwordTheme,
49
+ });
50
+ // let stripeLiveKey = await password({
51
+ // message: 'Enter Stripe Live Key:',
52
+ // mask: '*',
53
+ // theme: passwordTheme,
54
+ // })
55
+ await updateCLIStripeKeys(stripeTestKey, stripeTestKey, keyInfo.stripeFlowAuthKey);
56
+ }
57
+ else {
58
+ console.log(chalk.yellow("Okay, no worries. Go to the Autumn dashboard when you're ready!"));
59
+ }
60
+ }
61
+ storeToEnv(keyInfo.prodKey, keyInfo.sandboxKey);
62
+ console.log(chalk.green('Success! Keys have been stored locally. You may now use the CLI.'));
63
+ }
@@ -0,0 +1,3 @@
1
+ export default function Init({ config }: {
2
+ config: any;
3
+ }): Promise<void>;
@@ -0,0 +1,26 @@
1
+ import chalk from 'chalk';
2
+ import Pull from './pull.js';
3
+ import AuthCommand from './auth.js';
4
+ import { readFromEnv } from '../core/utils.js';
5
+ export default async function Init({ config }) {
6
+ // Try to read API key from .env first
7
+ let apiKey = readFromEnv();
8
+ if (apiKey) {
9
+ console.log(chalk.green('API key found. Pulling latest config...'));
10
+ await Pull({ config });
11
+ console.log(chalk.green('Project initialized and config pulled successfully!'));
12
+ return;
13
+ }
14
+ // If not found, run authentication
15
+ console.log(chalk.yellow('No API key found. Running authentication...'));
16
+ await AuthCommand();
17
+ // After authentication, try to read the key again
18
+ apiKey = readFromEnv();
19
+ if (apiKey) {
20
+ await Pull({ config });
21
+ console.log(chalk.green('Project initialized! You are now authenticated and config has been pulled.'));
22
+ }
23
+ else {
24
+ console.log(chalk.red('Authentication did not yield an API key. Please check your setup.'));
25
+ }
26
+ }
@@ -0,0 +1,3 @@
1
+ export default function Pull({ config }: {
2
+ config: any;
3
+ }): Promise<void>;
@@ -0,0 +1,28 @@
1
+ import chalk from "chalk";
2
+ import { getAllProducts, getFeatures, } from '../core/pull.js';
3
+ import { productBuilder } from '../core/builders/products.js';
4
+ import { featureBuilder } from '../core/builders/features.js';
5
+ import { writeConfig } from '../core/config.js';
6
+ import { importBuilder, exportBuilder } from '../core/builders/products.js';
7
+ import { snakeCaseToCamelCase } from '../core/utils.js';
8
+ export default async function Pull({ config }) {
9
+ console.log(chalk.green('Pulling products and features from Autumn...'));
10
+ const products = await getAllProducts();
11
+ const features = await getFeatures();
12
+ const productSnippets = products.map(product => productBuilder(product));
13
+ const featureSnippets = features.map(feature => featureBuilder(feature));
14
+ const autumnConfig = `
15
+ ${importBuilder()}
16
+
17
+ // Features
18
+ ${featureSnippets.join('\n')}
19
+
20
+ // Products
21
+ ${productSnippets.join('\n')}
22
+
23
+ // Remember to update this when you make changes!
24
+ ${exportBuilder(products.map(product => product.id), features.map(feature => snakeCaseToCamelCase(feature.id)))}
25
+ `;
26
+ writeConfig(autumnConfig);
27
+ console.log(chalk.green('Success! Config has been updated.'));
28
+ }
@@ -0,0 +1,5 @@
1
+ export default function Push({ config, yes, prod, }: {
2
+ config: any;
3
+ yes: boolean;
4
+ prod: boolean;
5
+ }): Promise<void>;
@@ -0,0 +1,47 @@
1
+ import chalk from 'chalk';
2
+ import { confirm } from '@inquirer/prompts';
3
+ import { upsertProduct, checkForDeletables, upsertFeature, } from '../core/push.js';
4
+ import { deleteFeature, deleteProduct } from '../core/api.js';
5
+ import { FRONTEND_URL } from '../constants.js';
6
+ export default async function Push({ config, yes, prod, }) {
7
+ let { features, products } = config;
8
+ let { featuresToDelete, productsToDelete } = await checkForDeletables(features, products);
9
+ for (let productId of productsToDelete) {
10
+ let shouldDelete = yes ||
11
+ (await confirm({
12
+ message: `Delete product [${productId}]?`,
13
+ }));
14
+ if (shouldDelete) {
15
+ await deleteProduct(productId);
16
+ console.log(chalk.green(`Product [${productId}] deleted successfully!`));
17
+ }
18
+ }
19
+ for (let feature of features) {
20
+ console.log(chalk.green(`Pushing feature [${feature.id}]`));
21
+ await upsertFeature(feature);
22
+ console.log(chalk.green(`Pushed feature [${feature.id}]`));
23
+ }
24
+ for (let product of products) {
25
+ console.log(chalk.green(`Pushing product [${product.id}]`));
26
+ await upsertProduct(product);
27
+ console.log(chalk.green(`Pushed product [${product.id}]`));
28
+ }
29
+ for (let featureId of featuresToDelete) {
30
+ let shouldDelete = yes ||
31
+ (await confirm({
32
+ message: `Delete feature [${featureId}]?`,
33
+ }));
34
+ if (shouldDelete) {
35
+ await deleteFeature(featureId);
36
+ console.log(chalk.green(`Feature [${featureId}] deleted successfully!`));
37
+ }
38
+ }
39
+ const env = prod ? 'prod' : 'sandbox';
40
+ console.log(chalk.magentaBright(`Success! Changes have been pushed to ${env}.`));
41
+ if (prod) {
42
+ console.log(chalk.magentaBright(`You can view the products at ${FRONTEND_URL}/products`));
43
+ }
44
+ else {
45
+ console.log(chalk.magentaBright(`You can view the products at ${FRONTEND_URL}/sandbox/products`));
46
+ }
47
+ }
@@ -0,0 +1,3 @@
1
+ export declare const FRONTEND_URL = "http://app.useautumn.com";
2
+ export declare const BACKEND_URL = "https://api.useautumn.com";
3
+ export declare const DEFAULT_CONFIG = "import {\n\tfeature,\n\tproduct,\n\tpriceItem,\n\tfeatureItem,\n\tpricedFeatureItem,\n} from 'autumn-js/compose';\n\nconst seats = feature({\n\tid: 'seats',\n\tname: 'Seats',\n\ttype: 'continuous_use',\n});\n\nconst messages = feature({\n\tid: 'messages',\n\tname: 'Messages',\n\ttype: 'single_use',\n});\n\nconst pro = product({\n\tid: 'pro',\n\tname: 'Pro',\n\titems: [\n\t\t// 500 messages per month\n\t\tfeatureItem({\n\t\t\tfeature_id: messages.id,\n\t\t\tincluded_usage: 500,\n\t\t\tinterval: 'month',\n\t\t}),\n\n\t\t// $10 per seat per month\n\t\tpricedFeatureItem({\n\t\t\tfeature_id: seats.id,\n\t\t\tprice: 10,\n\t\t\tinterval: 'month',\n\t\t}),\n\n\t\t// $50 / month\n\t\tpriceItem({\n\t\t\tprice: 50,\n\t\t\tinterval: 'month',\n\t\t}),\n\t],\n});\n\nexport default {\n\tfeatures: [seats, messages],\n\tproducts: [pro],\n};\n";
@@ -0,0 +1,55 @@
1
+ // export const FRONTEND_URL = 'http://localhost:3000';
2
+ // export const BACKEND_URL = 'http://localhost:8080';
3
+ export const FRONTEND_URL = 'http://app.useautumn.com';
4
+ export const BACKEND_URL = 'https://api.useautumn.com';
5
+ export const DEFAULT_CONFIG = `import {
6
+ feature,
7
+ product,
8
+ priceItem,
9
+ featureItem,
10
+ pricedFeatureItem,
11
+ } from 'autumn-js/compose';
12
+
13
+ const seats = feature({
14
+ id: 'seats',
15
+ name: 'Seats',
16
+ type: 'continuous_use',
17
+ });
18
+
19
+ const messages = feature({
20
+ id: 'messages',
21
+ name: 'Messages',
22
+ type: 'single_use',
23
+ });
24
+
25
+ const pro = product({
26
+ id: 'pro',
27
+ name: 'Pro',
28
+ items: [
29
+ // 500 messages per month
30
+ featureItem({
31
+ feature_id: messages.id,
32
+ included_usage: 500,
33
+ interval: 'month',
34
+ }),
35
+
36
+ // $10 per seat per month
37
+ pricedFeatureItem({
38
+ feature_id: seats.id,
39
+ price: 10,
40
+ interval: 'month',
41
+ }),
42
+
43
+ // $50 / month
44
+ priceItem({
45
+ price: 50,
46
+ interval: 'month',
47
+ }),
48
+ ],
49
+ });
50
+
51
+ export default {
52
+ features: [seats, messages],
53
+ products: [pro],
54
+ };
55
+ `;
@@ -0,0 +1,27 @@
1
+ export declare function request({ method, base, path, data, headers, customAuth, throwOnError, }: {
2
+ method: string;
3
+ base: string;
4
+ path: string;
5
+ data?: any;
6
+ headers?: any;
7
+ customAuth?: string;
8
+ throwOnError?: boolean;
9
+ }): Promise<any>;
10
+ export declare function internalRequest({ method, path, data, headers, customAuth, }: {
11
+ method: string;
12
+ path: string;
13
+ data?: any;
14
+ headers?: any;
15
+ customAuth?: string;
16
+ }): Promise<any>;
17
+ export declare function externalRequest({ method, path, data, headers, customAuth, throwOnError, }: {
18
+ method: string;
19
+ path: string;
20
+ data?: any;
21
+ headers?: any;
22
+ customAuth?: string;
23
+ throwOnError?: boolean;
24
+ }): Promise<any>;
25
+ export declare function deleteFeature(id: string): Promise<any>;
26
+ export declare function deleteProduct(id: string): Promise<any>;
27
+ export declare function updateCLIStripeKeys(stripeTestKey: string, stripeLiveKey: string, stripeFlowAuthKey: string): Promise<any>;
@@ -0,0 +1,75 @@
1
+ import axios from 'axios';
2
+ import chalk from 'chalk';
3
+ import { BACKEND_URL } from '../constants.js';
4
+ import { readFromEnv } from './utils.js';
5
+ const INTERNAL_BASE = BACKEND_URL;
6
+ const EXTERNAL_BASE = `${BACKEND_URL}/v1`;
7
+ export async function request({ method, base, path, data, headers, customAuth, throwOnError = true, }) {
8
+ const apiKey = readFromEnv();
9
+ try {
10
+ const response = await axios.request({
11
+ method,
12
+ url: `${base}${path}`,
13
+ data,
14
+ headers: {
15
+ 'Content-Type': 'application/json',
16
+ ...headers,
17
+ Authorization: customAuth || `Bearer ${apiKey}`,
18
+ },
19
+ });
20
+ return response.data;
21
+ }
22
+ catch (error) {
23
+ if (throwOnError) {
24
+ throw error;
25
+ }
26
+ console.error(chalk.red('Error occured when making API request:'), chalk.red(error.response.data.message || error.response.data.error));
27
+ process.exit(1);
28
+ }
29
+ }
30
+ export async function internalRequest({ method, path, data, headers, customAuth, }) {
31
+ return await request({
32
+ method,
33
+ base: INTERNAL_BASE,
34
+ path,
35
+ data,
36
+ headers,
37
+ customAuth,
38
+ });
39
+ }
40
+ export async function externalRequest({ method, path, data, headers, customAuth, throwOnError = false, }) {
41
+ return await request({
42
+ method,
43
+ base: EXTERNAL_BASE,
44
+ path,
45
+ data,
46
+ headers,
47
+ customAuth,
48
+ throwOnError,
49
+ });
50
+ }
51
+ export async function deleteFeature(id) {
52
+ return await externalRequest({
53
+ method: 'DELETE',
54
+ path: `/features/${id}`,
55
+ });
56
+ }
57
+ export async function deleteProduct(id) {
58
+ return await externalRequest({
59
+ method: 'DELETE',
60
+ path: `/products/${id}`,
61
+ });
62
+ }
63
+ export async function updateCLIStripeKeys(stripeTestKey, stripeLiveKey, stripeFlowAuthKey) {
64
+ return await internalRequest({
65
+ method: 'POST',
66
+ path: '/dev/cli/stripe',
67
+ data: {
68
+ stripeTestKey,
69
+ stripeLiveKey,
70
+ successUrl: 'https://useautumn.com',
71
+ defaultCurrency: 'usd',
72
+ },
73
+ customAuth: stripeFlowAuthKey,
74
+ });
75
+ }
@@ -0,0 +1 @@
1
+ export declare function getOTP(otp: string): Promise<any>;
@@ -0,0 +1,8 @@
1
+ import { internalRequest } from './api.js';
2
+ export async function getOTP(otp) {
3
+ const response = await internalRequest({
4
+ method: 'GET',
5
+ path: `/dev/otp/${otp}`,
6
+ });
7
+ return response;
8
+ }
@@ -0,0 +1,2 @@
1
+ import { Feature } from 'autumn-js/compose';
2
+ export declare function featureBuilder(feature: Feature): string;
@@ -0,0 +1,10 @@
1
+ import { snakeCaseToCamelCase } from '../utils.js';
2
+ export function featureBuilder(feature) {
3
+ const snippet = `
4
+ export const ${snakeCaseToCamelCase(feature.id)} = feature({
5
+ id: '${feature.id}',
6
+ name: '${feature.name}',
7
+ type: '${feature.type}',
8
+ })`;
9
+ return snippet;
10
+ }
@@ -0,0 +1,7 @@
1
+ import { ProductItem, Product } from 'autumn-js/compose';
2
+ export declare function importBuilder(): string;
3
+ export declare function exportBuilder(productIds: string[], featureIds: string[]): string;
4
+ export declare function productBuilder(product: Product): string;
5
+ export declare function pricedFeatureItemBuilder(item: ProductItem): string;
6
+ export declare function featureItemBuilder(item: ProductItem): string;
7
+ export declare function priceItemBuilder(item: ProductItem): string;
@@ -0,0 +1,71 @@
1
+ import { snakeCaseToCamelCase } from '../utils.js';
2
+ const ItemBuilders = {
3
+ priced_feature: pricedFeatureItemBuilder,
4
+ feature: featureItemBuilder,
5
+ price: priceItemBuilder,
6
+ };
7
+ export function importBuilder() {
8
+ return `
9
+ import {
10
+ feature,
11
+ product,
12
+ featureItem,
13
+ pricedFeatureItem,
14
+ priceItem,
15
+ } from 'autumn-js/compose';
16
+ `;
17
+ }
18
+ export function exportBuilder(productIds, featureIds) {
19
+ const snippet = `
20
+ export default {
21
+ products: [${productIds.map(id => `${id}Plan`).join(', ')}],
22
+ features: [${featureIds.map(id => `${id}`).join(', ')}]
23
+ }
24
+ `;
25
+ return snippet;
26
+ }
27
+ export function productBuilder(product) {
28
+ const snippet = `
29
+ export const ${product.id}Plan = product({
30
+ id: '${product.id}',
31
+ name: '${product.name}',
32
+ items: [${product.items
33
+ .map((item) => `${ItemBuilders[item.type](item)}`)
34
+ .join(' ')} ]
35
+ })
36
+ `;
37
+ return snippet;
38
+ }
39
+ // Item Builders
40
+ export function pricedFeatureItemBuilder(item) {
41
+ const intervalLine = item.interval == null ? '' : `\n interval: '${item.interval}',`;
42
+ const snippet = `
43
+ pricedFeatureItem({
44
+ feature_id: ${snakeCaseToCamelCase(item.feature_id)}.id,
45
+ price: ${item.price},${intervalLine}
46
+ included_usage: ${item.included_usage},
47
+ billing_units: ${item.billing_units},
48
+ usage_model: '${item.usage_model}',
49
+ }),
50
+ `;
51
+ return snippet;
52
+ }
53
+ export function featureItemBuilder(item) {
54
+ const intervalLine = item.interval == null ? '' : `\n interval: '${item.interval}',`;
55
+ const snippet = `
56
+ featureItem({
57
+ feature_id: ${snakeCaseToCamelCase(item.feature_id)}.id,
58
+ included_usage: ${item.included_usage},${intervalLine}
59
+ }),
60
+ `;
61
+ return snippet;
62
+ }
63
+ export function priceItemBuilder(item) {
64
+ const intervalLine = item.interval == null ? '' : `\n interval: '${item.interval}',`;
65
+ const snippet = `
66
+ priceItem({
67
+ price: ${item.price},${intervalLine}
68
+ }),
69
+ `;
70
+ return snippet;
71
+ }
@@ -0,0 +1,2 @@
1
+ export declare function loadAutumnConfigFile(): Promise<any>;
2
+ export declare function writeConfig(config: string): void;
@@ -0,0 +1,25 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import createJiti from 'jiti';
4
+ import { pathToFileURL } from 'url';
5
+ import { resolve } from 'path';
6
+ export async function loadAutumnConfigFile() {
7
+ const configPath = path.join(process.cwd(), 'autumn.config.ts');
8
+ const absolutePath = resolve(configPath);
9
+ const fileUrl = pathToFileURL(absolutePath).href;
10
+ // Dynamic import the TypeScript config file
11
+ const jiti = createJiti(import.meta.url);
12
+ const mod = await jiti.import(fileUrl);
13
+ const def = mod.default || mod;
14
+ if (!def.products || !Array.isArray(def.products)) {
15
+ throw new Error("You must export a products field that is an array of products.");
16
+ }
17
+ if (!def.features || !Array.isArray(def.features)) {
18
+ throw new Error("You must export a features field that is an array of products.");
19
+ }
20
+ return def;
21
+ }
22
+ export function writeConfig(config) {
23
+ const configPath = path.join(process.cwd(), 'autumn.config.ts');
24
+ fs.writeFileSync(configPath, config);
25
+ }
@@ -0,0 +1,17 @@
1
+ export declare function getProducts(ids: string[]): Promise<{
2
+ id: string;
3
+ name: string;
4
+ items: {
5
+ type?: "feature" | "priced_feature" | null | undefined;
6
+ feature_id?: string | null | undefined;
7
+ included_usage?: number | "inf" | null | undefined;
8
+ interval?: "minute" | "hour" | "day" | "week" | "month" | "quarter" | "semi_annual" | "year" | null | undefined;
9
+ usage_model?: "prepaid" | "pay_per_use" | null | undefined;
10
+ price?: number | null | undefined;
11
+ billing_units?: number | null | undefined;
12
+ }[];
13
+ is_add_on?: boolean | undefined;
14
+ is_default?: boolean | undefined;
15
+ }[]>;
16
+ export declare function getAllProducts(): Promise<any>;
17
+ export declare function getFeatures(): Promise<any>;
@@ -0,0 +1,22 @@
1
+ import { externalRequest } from './api.js';
2
+ export async function getProducts(ids) {
3
+ const products = await Promise.all(ids.map(id => externalRequest({
4
+ method: 'GET',
5
+ path: `/products/${id}`,
6
+ })));
7
+ return products.map(product => product);
8
+ }
9
+ export async function getAllProducts() {
10
+ const { list } = await externalRequest({
11
+ method: 'GET',
12
+ path: '/products',
13
+ });
14
+ return list.map(product => product);
15
+ }
16
+ export async function getFeatures() {
17
+ const { list } = await externalRequest({
18
+ method: 'GET',
19
+ path: '/features',
20
+ });
21
+ return list.map(feature => feature);
22
+ }
@@ -0,0 +1,7 @@
1
+ import { Feature, Product } from 'autumn-js/compose';
2
+ export declare function checkForDeletables(currentFeatures: Feature[], currentProducts: Product[]): Promise<{
3
+ featuresToDelete: any;
4
+ productsToDelete: any;
5
+ }>;
6
+ export declare function upsertFeature(feature: Feature): Promise<any>;
7
+ export declare function upsertProduct(product: Product): Promise<any>;
@@ -0,0 +1,67 @@
1
+ import { externalRequest } from './api.js';
2
+ import { getFeatures, getAllProducts } from './pull.js';
3
+ export async function checkForDeletables(currentFeatures, currentProducts) {
4
+ const features = await getFeatures(); // Get from AUTUMN
5
+ const featureIds = features.map(feature => feature.id);
6
+ const currentFeatureIds = currentFeatures.map(feature => feature.id);
7
+ const featuresToDelete = featureIds.filter(featureId => !currentFeatureIds.includes(featureId));
8
+ const products = await getAllProducts();
9
+ const productIds = products.map(product => product.id);
10
+ const currentProductIds = currentProducts.map(product => product.id);
11
+ const productsToDelete = productIds.filter(productId => !currentProductIds.includes(productId));
12
+ return { featuresToDelete, productsToDelete };
13
+ }
14
+ export async function upsertFeature(feature) {
15
+ try {
16
+ const response = await externalRequest({
17
+ method: 'POST',
18
+ path: `/features`,
19
+ data: feature,
20
+ throwOnError: true,
21
+ // data: {
22
+ // ...feature,
23
+ // config: {
24
+ // filters: [{property: '', operator: '', value: []}],
25
+ // usage_type: 'single_use',
26
+ // },
27
+ // },
28
+ });
29
+ return response.data;
30
+ }
31
+ catch (error) {
32
+ // If the first request fails, try posting to the specific feature ID endpoint
33
+ const response = await externalRequest({
34
+ method: 'POST',
35
+ path: `/features/${feature.id}`,
36
+ data: feature,
37
+ // data: {
38
+ // ...feature,
39
+ // config: {
40
+ // filters: [{property: '', operator: '', value: []}],
41
+ // usage_type: 'single_use',
42
+ // },
43
+ // },
44
+ });
45
+ return response.data;
46
+ }
47
+ }
48
+ export async function upsertProduct(product) {
49
+ try {
50
+ const response = await externalRequest({
51
+ method: 'POST',
52
+ path: `/products`,
53
+ data: product,
54
+ throwOnError: true,
55
+ });
56
+ return response.data;
57
+ }
58
+ catch (error) {
59
+ // If the first request fails, try posting to the specific product ID endpoint
60
+ const response = await externalRequest({
61
+ method: 'POST',
62
+ path: `/products/${product.id}`,
63
+ data: product,
64
+ });
65
+ return response.data;
66
+ }
67
+ }
@@ -0,0 +1,3 @@
1
+ export declare function snakeCaseToCamelCase(value: string): string;
2
+ export declare function storeToEnv(prodKey: string, sandboxKey: string): void;
3
+ export declare function readFromEnv(): string;
@@ -0,0 +1,26 @@
1
+ import fs from 'fs';
2
+ import chalk from 'chalk';
3
+ export function snakeCaseToCamelCase(value) {
4
+ return value.replace(/_([a-z])/g, (match, letter) => letter.toUpperCase());
5
+ }
6
+ export function storeToEnv(prodKey, sandboxKey) {
7
+ const envPath = `${process.cwd()}/.env`;
8
+ const envVars = `# AUTUMN_SECRET_KEY=${prodKey}\nAUTUMN_SECRET_KEY=${sandboxKey}\n`;
9
+ if (fs.existsSync(envPath)) {
10
+ fs.appendFileSync(envPath, envVars);
11
+ console.log(chalk.green('.env file found. Appended keys.'));
12
+ }
13
+ else {
14
+ fs.writeFileSync(envPath, envVars);
15
+ console.log(chalk.green('.env file not found. Created new .env file and wrote keys.'));
16
+ }
17
+ }
18
+ export function readFromEnv() {
19
+ const envPath = `${process.cwd()}/.env`;
20
+ if (!fs.existsSync(envPath)) {
21
+ return undefined;
22
+ }
23
+ const envContent = fs.readFileSync(envPath, 'utf-8');
24
+ const match = envContent.match(/^AUTUMN_SECRET_KEY=(.*)$/m);
25
+ return match ? match[1] : undefined;
26
+ }
package/package.json CHANGED
@@ -1,12 +1,65 @@
1
1
  {
2
- "name": "atmn",
3
- "version": "0.0.1",
4
- "description": "",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "keywords": [],
10
- "author": "John Yeo",
11
- "license": "ISC"
2
+ "name": "atmn",
3
+ "version": "0.0.2",
4
+ "license": "MIT",
5
+ "bin": "dist/cli.js",
6
+ "main": "dist/cli.js",
7
+ "type": "module",
8
+ "engines": {
9
+ "node": ">=16"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "dev": "tsc --watch",
14
+ "test2": "prettier --check . && xo && ava",
15
+ "test": "node ./dist/cli.js"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "package.json",
20
+ "README.md"
21
+ ],
22
+ "dependencies": {
23
+ "@inquirer/prompts": "^7.6.0",
24
+ "autumn-js": "workspace:*",
25
+ "axios": "^1.10.0",
26
+ "commander": "^14.0.0",
27
+ "dotenv": "^17.2.0",
28
+ "inquirer": "^12.7.0",
29
+ "jiti": "^2.4.2",
30
+ "open": "^10.1.2"
31
+ },
32
+ "devDependencies": {
33
+ "@sindresorhus/tsconfig": "^3.0.1",
34
+ "@types/node": "^24.0.10",
35
+ "@types/react": "^18.0.32",
36
+ "@vdemedes/prettier-config": "^2.0.1",
37
+ "ava": "^5.2.0",
38
+ "chalk": "^5.2.0",
39
+ "eslint-config-xo-react": "^0.27.0",
40
+ "eslint-plugin-react": "^7.32.2",
41
+ "eslint-plugin-react-hooks": "^4.6.0",
42
+ "ink-testing-library": "^3.0.0",
43
+ "prettier": "^2.8.7",
44
+ "ts-node": "^10.9.1",
45
+ "typescript": "^5.0.3",
46
+ "xo": "^0.53.1"
47
+ },
48
+ "ava": {
49
+ "extensions": {
50
+ "ts": "module",
51
+ "tsx": "module"
52
+ },
53
+ "nodeArguments": [
54
+ "--loader=ts-node/esm"
55
+ ]
56
+ },
57
+ "xo": {
58
+ "extends": "xo-react",
59
+ "prettier": true,
60
+ "rules": {
61
+ "react/prop-types": "off"
62
+ }
63
+ },
64
+ "prettier": "@vdemedes/prettier-config"
12
65
  }
package/readme.md ADDED
@@ -0,0 +1,25 @@
1
+ # cli
2
+
3
+ > This readme is automatically generated by [create-ink-app](https://github.com/vadimdemedes/create-ink-app)
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ $ npm install --global cli
9
+ ```
10
+
11
+ ## CLI
12
+
13
+ ```
14
+ $ cli --help
15
+
16
+ Usage
17
+ $ cli
18
+
19
+ Options
20
+ --name Your name
21
+
22
+ Examples
23
+ $ cli --name=Jane
24
+ Hello, Jane
25
+ ```