@swell/cli 1.0.2 → 2.0.1-alpha.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/LICENSE.md +21 -0
- package/README.md +2 -140
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +7 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +7 -0
- package/dist/app-command.d.ts +22 -0
- package/dist/app-command.js +45 -0
- package/dist/commands/app/_pull.d.ts +8 -0
- package/dist/commands/app/_pull.js +45 -0
- package/dist/commands/app/_uninstall.d.ts +12 -0
- package/dist/commands/app/_uninstall.js +45 -0
- package/dist/commands/app/info.d.ts +15 -0
- package/dist/commands/app/info.js +92 -0
- package/dist/commands/app/install.d.ts +17 -0
- package/dist/commands/app/install.js +177 -0
- package/dist/commands/app/push.d.ts +17 -0
- package/dist/commands/app/push.js +158 -0
- package/dist/commands/app/version.d.ts +18 -0
- package/dist/commands/app/version.js +156 -0
- package/dist/commands/app/watch.d.ts +15 -0
- package/dist/commands/app/watch.js +110 -0
- package/dist/commands/create/app.d.ts +21 -0
- package/dist/commands/create/app.js +210 -0
- package/dist/commands/create/content.d.ts +19 -0
- package/dist/commands/create/content.js +87 -0
- package/dist/commands/create/function.d.ts +25 -0
- package/dist/commands/create/function.js +191 -0
- package/dist/commands/create/index.d.ts +6 -0
- package/dist/commands/create/index.js +34 -0
- package/dist/commands/create/model.d.ts +19 -0
- package/dist/commands/create/model.js +87 -0
- package/dist/commands/create/notification.d.ts +25 -0
- package/dist/commands/create/notification.js +170 -0
- package/dist/commands/login.d.ts +22 -0
- package/dist/commands/login.js +77 -0
- package/dist/commands/logout.d.ts +8 -0
- package/dist/commands/logout.js +45 -0
- package/dist/commands/logs.d.ts +23 -0
- package/dist/commands/logs.js +207 -0
- package/dist/commands/switch.d.ts +10 -0
- package/dist/commands/switch.js +41 -0
- package/dist/commands/whoami.d.ts +7 -0
- package/dist/commands/whoami.js +24 -0
- package/dist/create-collection-command.d.ts +26 -0
- package/dist/create-collection-command.js +97 -0
- package/dist/create-command.d.ts +16 -0
- package/dist/create-command.js +59 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/api.d.ts +13 -0
- package/dist/lib/api.js +117 -0
- package/dist/lib/app-config.d.ts +29 -0
- package/dist/lib/app-config.js +127 -0
- package/dist/lib/apps/app-config.d.ts +42 -0
- package/dist/lib/apps/app-config.js +167 -0
- package/dist/lib/apps/index.d.ts +67 -0
- package/dist/lib/apps/index.js +167 -0
- package/dist/lib/apps/paths.d.ts +18 -0
- package/dist/lib/apps/paths.js +54 -0
- package/dist/lib/bundle.d.ts +1 -0
- package/dist/lib/bundle.js +40 -0
- package/dist/lib/config.d.ts +37 -0
- package/dist/lib/config.js +131 -0
- package/dist/lib/constants.d.ts +5 -0
- package/dist/lib/constants.js +17 -0
- package/dist/lib/create/content.d.ts +23 -0
- package/dist/lib/create/content.js +40 -0
- package/dist/lib/create/function.d.ts +17 -0
- package/dist/lib/create/function.js +36 -0
- package/dist/lib/create/index.d.ts +13 -0
- package/dist/lib/create/index.js +20 -0
- package/dist/lib/create/model.d.ts +22 -0
- package/dist/lib/create/model.js +30 -0
- package/dist/lib/create/notification.d.ts +25 -0
- package/dist/lib/create/notification.js +1 -0
- package/dist/lib/info.d.ts +38 -0
- package/dist/lib/info.js +91 -0
- package/dist/lib/logs/index.d.ts +23 -0
- package/dist/lib/logs/index.js +88 -0
- package/dist/lib/logs/line-output.d.ts +5 -0
- package/dist/lib/logs/line-output.js +13 -0
- package/dist/lib/logs/output.d.ts +7 -0
- package/dist/lib/logs/output.js +8 -0
- package/dist/lib/logs/table-output.d.ts +11 -0
- package/dist/lib/logs/table-output.js +128 -0
- package/dist/lib/sessions.d.ts +9 -0
- package/dist/lib/sessions.js +110 -0
- package/dist/lib/stores.d.ts +63 -0
- package/dist/lib/stores.js +173 -0
- package/dist/lib/style.d.ts +24 -0
- package/dist/lib/style.js +29 -0
- package/dist/lib/swell-function-wrapper.d.ts +88 -0
- package/dist/lib/swell-function-wrapper.js +302 -0
- package/dist/remote-app-command.d.ts +36 -0
- package/dist/remote-app-command.js +367 -0
- package/dist/swell-command.d.ts +21 -0
- package/dist/swell-command.js +43 -0
- package/oclif.manifest.json +1085 -0
- package/package.json +81 -43
- package/LICENSE +0 -674
- package/dist/bin/multipart-parser-85ef9626.js +0 -456
- package/dist/bin/swell-0f71f07d.js +0 -30670
- package/dist/bin/swell.js +0 -24
- package/dist/man/swell.1 +0 -85
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CreateCommand } from './create-command.js';
|
|
2
|
+
/**
|
|
3
|
+
* A base class for Swell CLI Create [content|models] command input.
|
|
4
|
+
*
|
|
5
|
+
* This class extends the `CreateCommand` class and adds:
|
|
6
|
+
*
|
|
7
|
+
* - promptCollection: Get collection name and description
|
|
8
|
+
*/
|
|
9
|
+
export declare abstract class CreateCollectionCommand extends CreateCommand {
|
|
10
|
+
static baseFlags: {
|
|
11
|
+
'app-path': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
12
|
+
};
|
|
13
|
+
static args: {
|
|
14
|
+
collection: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
|
|
15
|
+
};
|
|
16
|
+
static flags: {
|
|
17
|
+
label: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
18
|
+
description: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
19
|
+
overwrite: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
20
|
+
};
|
|
21
|
+
promptCollection(): Promise<{
|
|
22
|
+
label: string;
|
|
23
|
+
description: string;
|
|
24
|
+
collection: string;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { input, select, Separator } from '@inquirer/prompts';
|
|
2
|
+
import { Args, Flags } from '@oclif/core';
|
|
3
|
+
import { CreateCommand } from './create-command.js';
|
|
4
|
+
import { toCollectionName, toCollectionLabel } from './lib/create/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* A base class for Swell CLI Create [content|models] command input.
|
|
7
|
+
*
|
|
8
|
+
* This class extends the `CreateCommand` class and adds:
|
|
9
|
+
*
|
|
10
|
+
* - promptCollection: Get collection name and description
|
|
11
|
+
*/
|
|
12
|
+
export class CreateCollectionCommand extends CreateCommand {
|
|
13
|
+
// base flags for all AppCommands
|
|
14
|
+
// needs to be in a ES2021 format: https://github.com/oclif/oclif/issues/1100
|
|
15
|
+
static baseFlags = {
|
|
16
|
+
'app-path': Flags.string({
|
|
17
|
+
description: 'path to the app directory',
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
static args = {
|
|
21
|
+
collection: Args.string({
|
|
22
|
+
default: '',
|
|
23
|
+
description: 'collection to create or apply to, i.e. products',
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
static flags = {
|
|
27
|
+
label: Flags.string({
|
|
28
|
+
char: 'l',
|
|
29
|
+
default: '',
|
|
30
|
+
description: 'the collection label',
|
|
31
|
+
}),
|
|
32
|
+
description: Flags.string({
|
|
33
|
+
char: 'd',
|
|
34
|
+
default: '',
|
|
35
|
+
description: 'the collection description',
|
|
36
|
+
}),
|
|
37
|
+
overwrite: Flags.boolean({
|
|
38
|
+
char: 'o',
|
|
39
|
+
default: false,
|
|
40
|
+
description: 'overwrite existing collection configuration file',
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
async promptCollection() {
|
|
44
|
+
const { args, flags } = await this.parse(CreateCollectionCommand);
|
|
45
|
+
let { label, description } = flags;
|
|
46
|
+
let { collection } = args;
|
|
47
|
+
let isStandardCollection = false;
|
|
48
|
+
let collectionInput = collection;
|
|
49
|
+
if (!collection) {
|
|
50
|
+
const collections = await this.getCollectionOptions();
|
|
51
|
+
collectionInput = await select({
|
|
52
|
+
message: 'Where will content be stored?',
|
|
53
|
+
choices: [
|
|
54
|
+
{ name: 'Create a new collection', value: '' },
|
|
55
|
+
new Separator(),
|
|
56
|
+
...collections.map((collectionName) => ({
|
|
57
|
+
name: collectionName,
|
|
58
|
+
value: collectionName,
|
|
59
|
+
})),
|
|
60
|
+
],
|
|
61
|
+
pageSize: 25,
|
|
62
|
+
});
|
|
63
|
+
if (collectionInput) {
|
|
64
|
+
isStandardCollection = true;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
collectionInput = await input({
|
|
68
|
+
message: 'Collection name',
|
|
69
|
+
default: 'special-things',
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// we replace unwanted characters with underscores to make things easier
|
|
73
|
+
// for the inflection library
|
|
74
|
+
collection = toCollectionName(collectionInput);
|
|
75
|
+
}
|
|
76
|
+
if (!label && !isStandardCollection) {
|
|
77
|
+
label = await input({
|
|
78
|
+
message: 'Collection label',
|
|
79
|
+
default: toCollectionLabel(collection),
|
|
80
|
+
});
|
|
81
|
+
label = toCollectionLabel(label);
|
|
82
|
+
}
|
|
83
|
+
description =
|
|
84
|
+
description ||
|
|
85
|
+
(await input({
|
|
86
|
+
message: isStandardCollection
|
|
87
|
+
? `Describe the purpose of fields being added to ${collection}`
|
|
88
|
+
: 'Describe what this collection is for',
|
|
89
|
+
default: 'Special things for special people',
|
|
90
|
+
}));
|
|
91
|
+
return {
|
|
92
|
+
label,
|
|
93
|
+
description,
|
|
94
|
+
collection,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AppCommand } from './app-command.js';
|
|
2
|
+
import { ConfigType } from './lib/apps/index.js';
|
|
3
|
+
import { CreateFileParams } from './lib/create/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* A base class for Swell CLI Create commands for file and input handling.
|
|
6
|
+
*
|
|
7
|
+
* This class extends the `AppCommand` class and adds:
|
|
8
|
+
*
|
|
9
|
+
* - createType: Type of Config to generate file path
|
|
10
|
+
* - createFile: File creation, including general prompts
|
|
11
|
+
*/
|
|
12
|
+
export declare abstract class CreateCommand extends AppCommand {
|
|
13
|
+
protected createType: '' | ConfigType;
|
|
14
|
+
protected createFile({ extension, fileBody, fileName }: CreateFileParams, overwrite: boolean, shouldConfirm?: boolean): Promise<boolean | undefined>;
|
|
15
|
+
protected getCollectionOptions(): Promise<any>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { confirm } from '@inquirer/prompts';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { AppCommand } from './app-command.js';
|
|
4
|
+
import { ConfigPaths, fileExists, writeFile, writeJsonFile, } from './lib/apps/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* A base class for Swell CLI Create commands for file and input handling.
|
|
7
|
+
*
|
|
8
|
+
* This class extends the `AppCommand` class and adds:
|
|
9
|
+
*
|
|
10
|
+
* - createType: Type of Config to generate file path
|
|
11
|
+
* - createFile: File creation, including general prompts
|
|
12
|
+
*/
|
|
13
|
+
export class CreateCommand extends AppCommand {
|
|
14
|
+
createType = '';
|
|
15
|
+
async createFile({ extension = 'json', fileBody, fileName }, overwrite, shouldConfirm = true) {
|
|
16
|
+
const isJson = extension === 'json';
|
|
17
|
+
const filePath = path.join(this.appPath, ConfigPaths[this.createType.toUpperCase()], `${fileName}.${extension}`);
|
|
18
|
+
if (shouldConfirm) {
|
|
19
|
+
this.log(`\nCreating app ${this.createType} in ${filePath}`);
|
|
20
|
+
this.log(`\n${isJson ? JSON.stringify(fileBody, null, 4) : fileBody}\n`);
|
|
21
|
+
if (fileExists(filePath) && !overwrite) {
|
|
22
|
+
const continueWrite = await confirm({
|
|
23
|
+
message: `This will overwrite an existing ${this.createType} file. Ok to continue?`,
|
|
24
|
+
});
|
|
25
|
+
if (!continueWrite) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const confirmCreate = await confirm({
|
|
31
|
+
message: 'Ok to continue?',
|
|
32
|
+
});
|
|
33
|
+
if (!confirmCreate) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
typeof fileBody === 'string'
|
|
39
|
+
? await writeFile(filePath, fileBody)
|
|
40
|
+
: await writeJsonFile(filePath, fileBody);
|
|
41
|
+
this.log(`${filePath} created.`);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
async getCollectionOptions() {
|
|
45
|
+
const models = await this.api.post({ adminPath: `/data/$get/:models` }, {
|
|
46
|
+
body: {
|
|
47
|
+
limit: 1000,
|
|
48
|
+
client_id: null,
|
|
49
|
+
app_id: null,
|
|
50
|
+
abstract: { $ne: true },
|
|
51
|
+
deprecated: { $ne: true },
|
|
52
|
+
name: { $nin: ['events', 'notifications', 'settings'] },
|
|
53
|
+
sort: 'namespace asc, name asc',
|
|
54
|
+
fields: 'name, namespace',
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
return (models?.results?.map((model) => model.namespace ? `${model.namespace}/${model.name}` : model.name) || []);
|
|
58
|
+
}
|
|
59
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default class Api {
|
|
2
|
+
private secretKey;
|
|
3
|
+
private storeId;
|
|
4
|
+
private envId;
|
|
5
|
+
constructor(storeId?: string, envId?: string, secretKey?: string);
|
|
6
|
+
setStoreEnv(storeId: string, envId?: string): void;
|
|
7
|
+
api(pathOpts: Api.Paths, options: Api.RequestOptions): Promise<any>;
|
|
8
|
+
delete(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
|
|
9
|
+
get(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
|
|
10
|
+
isAdmin(): boolean;
|
|
11
|
+
post(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
|
|
12
|
+
put(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
|
|
13
|
+
}
|
package/dist/lib/api.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
|
+
import config from './config.js';
|
|
3
|
+
import { getAdminApiBaseUrl, API_BASE_URL } from './constants.js';
|
|
4
|
+
const defaultHeaders = (opts) => ({
|
|
5
|
+
'Content-Type': 'application/json',
|
|
6
|
+
...opts,
|
|
7
|
+
});
|
|
8
|
+
const authenticatedHeaders = (storeId, opts = {}) => {
|
|
9
|
+
let { secretKey, sessionId, ...moreOpts } = opts;
|
|
10
|
+
sessionId ??= config.getSessionId(storeId);
|
|
11
|
+
if (secretKey) {
|
|
12
|
+
return defaultHeaders({
|
|
13
|
+
Authorization: `Basic ${Buffer.from(`${storeId}:${secretKey}`).toString('base64')}`,
|
|
14
|
+
...moreOpts,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
if (sessionId) {
|
|
18
|
+
return defaultHeaders({
|
|
19
|
+
'X-Session': sessionId,
|
|
20
|
+
...moreOpts,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return defaultHeaders(moreOpts);
|
|
24
|
+
};
|
|
25
|
+
export default class Api {
|
|
26
|
+
secretKey;
|
|
27
|
+
storeId;
|
|
28
|
+
envId;
|
|
29
|
+
constructor(storeId, envId, secretKey) {
|
|
30
|
+
this.storeId = storeId;
|
|
31
|
+
this.envId = envId;
|
|
32
|
+
this.secretKey = secretKey;
|
|
33
|
+
}
|
|
34
|
+
setStoreEnv(storeId, envId) {
|
|
35
|
+
this.storeId = storeId;
|
|
36
|
+
this.envId = envId;
|
|
37
|
+
}
|
|
38
|
+
async api(pathOpts, options) {
|
|
39
|
+
const storeId = this.storeId || config.getDefaultStore();
|
|
40
|
+
let path;
|
|
41
|
+
const fetchOptions = {
|
|
42
|
+
...options,
|
|
43
|
+
headers: {
|
|
44
|
+
'User-Agent': 'swell-cli/2.0',
|
|
45
|
+
...options?.headers,
|
|
46
|
+
...(this.envId ? { 'Swell-Env': this.envId } : undefined),
|
|
47
|
+
...authenticatedHeaders(storeId, {
|
|
48
|
+
secretKey: this.secretKey,
|
|
49
|
+
sessionId: options.sessionId,
|
|
50
|
+
}),
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
path = this.isAdmin()
|
|
54
|
+
? `${getAdminApiBaseUrl(storeId)}/${pathOpts.adminPath}`
|
|
55
|
+
: `${API_BASE_URL}/${pathOpts.backendPath}`;
|
|
56
|
+
if (options.query) {
|
|
57
|
+
const query = new URLSearchParams(options.query);
|
|
58
|
+
path += `?${query.toString()}`;
|
|
59
|
+
}
|
|
60
|
+
path = path
|
|
61
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
62
|
+
.replace('${STORE_ID}', storeId) // replace store id in template string
|
|
63
|
+
.replaceAll(/([^:]\/)\/+/g, '$1'); // remove duplicate slashes
|
|
64
|
+
if (!path)
|
|
65
|
+
throw new Error('Operation not supported.');
|
|
66
|
+
const resp = await fetch(path, fetchOptions);
|
|
67
|
+
const respData = await resp.text();
|
|
68
|
+
if (!resp.ok)
|
|
69
|
+
throw new Error(respData);
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(respData);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
// respData could be "{}" (length: >=2) but it shouldn't be "{", " " (whitespace - length: 1) or
|
|
75
|
+
// "" (empty response - length: 0) while logging the error.
|
|
76
|
+
if (respData.length >= 2)
|
|
77
|
+
console.log(error);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async delete(pathOpts, options = {}) {
|
|
82
|
+
// ensure post body is stringified
|
|
83
|
+
if (options.body)
|
|
84
|
+
options.body = JSON.stringify(options.body);
|
|
85
|
+
return this.api(pathOpts, {
|
|
86
|
+
method: 'DELETE',
|
|
87
|
+
...options,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
async get(pathOpts, options = {}) {
|
|
91
|
+
return this.api(pathOpts, {
|
|
92
|
+
method: 'GET',
|
|
93
|
+
...options,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
isAdmin() {
|
|
97
|
+
return !this.secretKey;
|
|
98
|
+
}
|
|
99
|
+
async post(pathOpts, options = {}) {
|
|
100
|
+
// ensure post body is stringified
|
|
101
|
+
if (options.body)
|
|
102
|
+
options.body = JSON.stringify(options.body);
|
|
103
|
+
return this.api(pathOpts, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
...options,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async put(pathOpts, options = {}) {
|
|
109
|
+
// ensure post body is stringified
|
|
110
|
+
if (options.body)
|
|
111
|
+
options.body = JSON.stringify(options.body);
|
|
112
|
+
return this.api(pathOpts, {
|
|
113
|
+
method: 'PUT',
|
|
114
|
+
...options,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { App } from './apps';
|
|
2
|
+
/**
|
|
3
|
+
* Checks if the app config file exists.
|
|
4
|
+
*
|
|
5
|
+
* @param configFilePath - the path to the app config file
|
|
6
|
+
* @returns boolean
|
|
7
|
+
* @throws Error
|
|
8
|
+
*/
|
|
9
|
+
declare function appConfigFileExists(configFilePath?: string): boolean;
|
|
10
|
+
declare function writeRcFile(path: string, data: any): Promise<void>;
|
|
11
|
+
declare function readRcFile(path: string): App;
|
|
12
|
+
/**
|
|
13
|
+
* Returns a new configuration instance in the current working directory or
|
|
14
|
+
* a parent directory.
|
|
15
|
+
*
|
|
16
|
+
* @param configDirPath - the path to the app config directory. If not provided,
|
|
17
|
+
* the config file will be searched for in the current working directory or
|
|
18
|
+
* a parent directory.
|
|
19
|
+
*
|
|
20
|
+
* @returns Conf
|
|
21
|
+
*/
|
|
22
|
+
declare function config(configDirPath?: string): LocalApp;
|
|
23
|
+
/**
|
|
24
|
+
* Returns a new configuration instance in the current working directory only.
|
|
25
|
+
*
|
|
26
|
+
* @returns Conf
|
|
27
|
+
*/
|
|
28
|
+
declare function newConfig(path?: string): any;
|
|
29
|
+
export { appConfigFileExists, config as default, newConfig, readRcFile, writeRcFile, };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App configuration for local development of Swell Apps.
|
|
3
|
+
*/
|
|
4
|
+
import Conf from 'conf';
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
const schema = {
|
|
8
|
+
id: { type: 'string' },
|
|
9
|
+
version: { type: 'string' },
|
|
10
|
+
name: { type: 'string' },
|
|
11
|
+
description: { type: 'string' },
|
|
12
|
+
type: { type: 'string' },
|
|
13
|
+
permissions: { type: 'array' },
|
|
14
|
+
};
|
|
15
|
+
const APP_FILE_NAME = 'swell';
|
|
16
|
+
const APP_FILE_EXT = 'json';
|
|
17
|
+
/**
|
|
18
|
+
* Builds the path to the app config file. If no directory is provided, the
|
|
19
|
+
* current working directory is used.
|
|
20
|
+
*
|
|
21
|
+
* @param dir - the directory to use for the config file path
|
|
22
|
+
* @returns string
|
|
23
|
+
*/
|
|
24
|
+
function appConfigFile(dir = '') {
|
|
25
|
+
if (!dir) {
|
|
26
|
+
dir = process.cwd();
|
|
27
|
+
}
|
|
28
|
+
return path.join(dir, [APP_FILE_NAME, '.', APP_FILE_EXT].join(''));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Checks if the app config file exists.
|
|
32
|
+
*
|
|
33
|
+
* @param configFilePath - the path to the app config file
|
|
34
|
+
* @returns boolean
|
|
35
|
+
* @throws Error
|
|
36
|
+
*/
|
|
37
|
+
function appConfigFileExists(configFilePath = '') {
|
|
38
|
+
// if the config file path is not provided, try to find it as the default
|
|
39
|
+
if (!configFilePath) {
|
|
40
|
+
configFilePath = appConfigFile();
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
// eslint-disable-next-line no-bitwise
|
|
44
|
+
fs.accessSync(configFilePath, fs.constants.R_OK | fs.constants.W_OK);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (error.code !== 'ENOENT') {
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Finds the app config file in the current directory or a parent directory.
|
|
56
|
+
* If the file is not found, an error is thrown.
|
|
57
|
+
*
|
|
58
|
+
* @param dir - the directory to start searching from
|
|
59
|
+
* @returns string
|
|
60
|
+
* @throws Error
|
|
61
|
+
*/
|
|
62
|
+
function appConfigFilePath(dir = '') {
|
|
63
|
+
const configFilePath = appConfigFile(dir);
|
|
64
|
+
if (appConfigFileExists(configFilePath)) {
|
|
65
|
+
return configFilePath;
|
|
66
|
+
}
|
|
67
|
+
// if the file doesn't exist, try to find it in the parent directory
|
|
68
|
+
// until we reach the root directory
|
|
69
|
+
if (dir && dir === path.parse(dir).root) {
|
|
70
|
+
return '';
|
|
71
|
+
}
|
|
72
|
+
return appConfigFilePath(path.resolve(dir, '..'));
|
|
73
|
+
}
|
|
74
|
+
async function writeRcFile(path, data) {
|
|
75
|
+
let dataToWrite = '';
|
|
76
|
+
for (const [key, value] of Object.entries(data)) {
|
|
77
|
+
dataToWrite += `${key}=${value}\n`;
|
|
78
|
+
}
|
|
79
|
+
fs.writeFileSync(path, dataToWrite);
|
|
80
|
+
}
|
|
81
|
+
function readRcFile(path) {
|
|
82
|
+
const data = {};
|
|
83
|
+
const dataFromFile = fs.readFileSync(path, 'utf8');
|
|
84
|
+
const lines = dataFromFile.split('\n');
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
const [key, value] = line.split('=');
|
|
87
|
+
if (key && value) {
|
|
88
|
+
data[key] = value;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return data;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Returns a new configuration instance in the current working directory or
|
|
95
|
+
* a parent directory.
|
|
96
|
+
*
|
|
97
|
+
* @param configDirPath - the path to the app config directory. If not provided,
|
|
98
|
+
* the config file will be searched for in the current working directory or
|
|
99
|
+
* a parent directory.
|
|
100
|
+
*
|
|
101
|
+
* @returns Conf
|
|
102
|
+
*/
|
|
103
|
+
function config(configDirPath = '') {
|
|
104
|
+
const dirPath = configDirPath || path.dirname(appConfigFilePath());
|
|
105
|
+
const config = new Conf({
|
|
106
|
+
configName: APP_FILE_NAME,
|
|
107
|
+
cwd: dirPath,
|
|
108
|
+
schema,
|
|
109
|
+
});
|
|
110
|
+
// for convenience, we add custom properties to the Conf object
|
|
111
|
+
// we prefix them with "sw" to avoid conflicts with Conf's properties
|
|
112
|
+
config.swDirPath = dirPath;
|
|
113
|
+
return config;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Returns a new configuration instance in the current working directory only.
|
|
117
|
+
*
|
|
118
|
+
* @returns Conf
|
|
119
|
+
*/
|
|
120
|
+
function newConfig(path) {
|
|
121
|
+
return new Conf({
|
|
122
|
+
configName: APP_FILE_NAME,
|
|
123
|
+
cwd: `${process.cwd()}${path ? `/${path}` : ''}`,
|
|
124
|
+
schema,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
export { appConfigFileExists, config as default, newConfig, readRcFile, writeRcFile, };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ConfigType } from './index.js';
|
|
2
|
+
declare class IgnoringFileError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
declare class FunctionProcessingError extends Error {
|
|
6
|
+
originalError: Error;
|
|
7
|
+
constructor(message: string, error: Error);
|
|
8
|
+
}
|
|
9
|
+
type AppConfigFile = {
|
|
10
|
+
data?: string;
|
|
11
|
+
date_uploaded?: string;
|
|
12
|
+
ext?: string;
|
|
13
|
+
id?: string;
|
|
14
|
+
length?: number;
|
|
15
|
+
md5?: string;
|
|
16
|
+
url?: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* An app configuration as defined by the Swell API. This implies that the
|
|
20
|
+
* app & the configuration are saved to the API.
|
|
21
|
+
*/
|
|
22
|
+
declare abstract class AppConfig {
|
|
23
|
+
date_created?: string;
|
|
24
|
+
file?: AppConfigFile;
|
|
25
|
+
fileData?: string;
|
|
26
|
+
id?: string;
|
|
27
|
+
/** The local path of a remote AppConfig */
|
|
28
|
+
localPath?: string;
|
|
29
|
+
name?: string;
|
|
30
|
+
parent_id?: string;
|
|
31
|
+
slug?: string;
|
|
32
|
+
values: {
|
|
33
|
+
[key: string]: any;
|
|
34
|
+
};
|
|
35
|
+
constructor(attrs?: Partial<AppConfig>);
|
|
36
|
+
static create(attrs?: Partial<AppConfig>): AppConfig;
|
|
37
|
+
postData(): Promise<any>;
|
|
38
|
+
/** build the JSON used when making requests (usually push) to the API */
|
|
39
|
+
protected abstract preparePostData(postData: any): Promise<any>;
|
|
40
|
+
abstract type: ConfigType;
|
|
41
|
+
}
|
|
42
|
+
export { AppConfig, FunctionProcessingError, IgnoringFileError };
|