@inizioevoke/veeva-astroclm-core 1.0.0
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/.editorconfig +12 -0
- package/build.ts +38 -0
- package/dist/apps/index.d.ts +2 -0
- package/dist/apps/index.js +2 -0
- package/dist/apps/page-manager/bulk-create.d.ts +1 -0
- package/dist/apps/page-manager/bulk-create.js +120 -0
- package/dist/apps/page-manager/index.d.ts +5 -0
- package/dist/apps/page-manager/index.js +24 -0
- package/dist/apps/page-manager/single-create.d.ts +1 -0
- package/dist/apps/page-manager/single-create.js +78 -0
- package/dist/apps/page-manager/templates/contents.astro.txt +12 -0
- package/dist/apps/page-manager/templates/page.astro.txt +4 -0
- package/dist/apps/page-manager/utils.d.ts +10 -0
- package/dist/apps/page-manager/utils.js +57 -0
- package/dist/apps/utils.d.ts +5 -0
- package/dist/apps/utils.js +43 -0
- package/dist/apps/veeva-config-manager/create.d.ts +1 -0
- package/dist/apps/veeva-config-manager/create.js +136 -0
- package/dist/apps/veeva-config-manager/index.d.ts +3 -0
- package/dist/apps/veeva-config-manager/index.js +16 -0
- package/dist/apps/veeva-config-manager/templates/config.ts.txt +60 -0
- package/dist/env/schema.d.ts +15 -0
- package/dist/env/schema.js +28 -0
- package/dist/lib/const.d.ts +15 -0
- package/dist/lib/const.js +14 -0
- package/dist/lib/env.d.ts +8 -0
- package/dist/lib/env.js +17 -0
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/index.js +5 -0
- package/dist/lib/logger.d.ts +9 -0
- package/dist/lib/logger.js +72 -0
- package/dist/lib/parse-argv.d.ts +31 -0
- package/dist/lib/parse-argv.js +109 -0
- package/dist/lib/parse-env.d.ts +45 -0
- package/dist/lib/parse-env.js +124 -0
- package/dist/lib/utils.d.ts +8 -0
- package/dist/lib/utils.js +37 -0
- package/dist/tasks/copy-files.d.ts +9 -0
- package/dist/tasks/copy-files.js +15 -0
- package/dist/tasks/create-csv.d.ts +15 -0
- package/dist/tasks/create-csv.js +186 -0
- package/dist/tasks/create-zips.d.ts +5 -0
- package/dist/tasks/create-zips.js +16 -0
- package/dist/tasks/deploy.d.ts +10 -0
- package/dist/tasks/deploy.js +49 -0
- package/dist/tasks/generate-thumbs.d.ts +12 -0
- package/dist/tasks/generate-thumbs.js +152 -0
- package/dist/tasks/index.d.ts +1 -0
- package/dist/tasks/index.js +1 -0
- package/dist/tasks/update-shared.d.ts +5 -0
- package/dist/tasks/update-shared.js +41 -0
- package/dist/types/config.d.ts +52 -0
- package/dist/types/env.d.ts +7 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/veeva.d.ts +58 -0
- package/dist/veeva/slideManager.d.ts +33 -0
- package/dist/veeva/slideManager.js +120 -0
- package/package.json +39 -0
- package/src/apps/index.ts +2 -0
- package/src/apps/page-manager/bulk-create.ts +131 -0
- package/src/apps/page-manager/index.ts +31 -0
- package/src/apps/page-manager/single-create.ts +97 -0
- package/src/apps/page-manager/templates/contents.astro.txt +12 -0
- package/src/apps/page-manager/templates/page.astro.txt +4 -0
- package/src/apps/page-manager/utils.ts +70 -0
- package/src/apps/utils.ts +47 -0
- package/src/apps/veeva-config-manager/create.ts +153 -0
- package/src/apps/veeva-config-manager/index.ts +20 -0
- package/src/apps/veeva-config-manager/templates/config.ts.txt +60 -0
- package/src/env/schema.ts +43 -0
- package/src/lib/const.ts +17 -0
- package/src/lib/env.ts +27 -0
- package/src/lib/index.ts +5 -0
- package/src/lib/logger.ts +84 -0
- package/src/lib/parse-argv.ts +125 -0
- package/src/lib/parse-env.ts +147 -0
- package/src/lib/utils.ts +37 -0
- package/src/tasks/copy-files.ts +29 -0
- package/src/tasks/create-csv.ts +259 -0
- package/src/tasks/create-zips.ts +21 -0
- package/src/tasks/deploy.ts +72 -0
- package/src/tasks/generate-thumbs.ts +179 -0
- package/src/tasks/index.ts +1 -0
- package/src/tasks/update-shared.ts +49 -0
- package/src/types/config.d.ts +52 -0
- package/src/types/env.d.ts +7 -0
- package/src/types/index.d.ts +9 -0
- package/src/types/veeva.d.ts +58 -0
- package/src/veeva/readme.md +77 -0
- package/src/veeva/slideManager.ts +139 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { readFile, writeFile, readdir } from 'node:fs/promises';
|
|
2
|
+
import { join, resolve as resolvePath } from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { text, select, multiselect, note, spinner, outro } from '@clack/prompts';
|
|
5
|
+
import { sleep, handleCanceled, cleanInput, generateRandomId, titleCase } from '../utils.js';
|
|
6
|
+
|
|
7
|
+
import type { Actions, iOSResolution, CrmTarget } from '../../types';
|
|
8
|
+
|
|
9
|
+
const __dirname = import.meta.dirname;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export default async function(rootDir: string) {
|
|
14
|
+
|
|
15
|
+
const product = handleCanceled(await text({
|
|
16
|
+
message: 'Enter the product name',
|
|
17
|
+
validate: (value: string | undefined) => {
|
|
18
|
+
if (!value?.trim()) return 'Product name is required';
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
const presentation = handleCanceled(await text({
|
|
24
|
+
message: 'Enter a presentation name',
|
|
25
|
+
validate: (value: string | undefined) => {
|
|
26
|
+
if (!value?.trim()) return 'Presentation name is required';
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
const presentationId = handleCanceled(await text({
|
|
32
|
+
message: 'Enter a presentation ID',
|
|
33
|
+
initialValue: presentation.toString().toLowerCase().replace(/[^0-9a-z_]/g, '_'),
|
|
34
|
+
validate: (value: string | undefined) => {
|
|
35
|
+
if (!value?.trim()) return 'Presentation ID is required';
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
const dimensions = handleCanceled(await select({
|
|
41
|
+
message: 'Select the IVA dimensions',
|
|
42
|
+
options: [{
|
|
43
|
+
label: '1024 x 768',
|
|
44
|
+
value: '1024_768'
|
|
45
|
+
}, {
|
|
46
|
+
label: '1366 x 1024',
|
|
47
|
+
value: '1366_1024'
|
|
48
|
+
}, {
|
|
49
|
+
label: '1376 x 1032',
|
|
50
|
+
value: '1376_1032'
|
|
51
|
+
}],
|
|
52
|
+
initialValue: '1366_1024'
|
|
53
|
+
}));
|
|
54
|
+
const [ width, height ] = dimensions.split('_');
|
|
55
|
+
|
|
56
|
+
const iosResolution = handleCanceled<iOSResolution>(await select({
|
|
57
|
+
message: 'Select the iOS resolution',
|
|
58
|
+
options: [{
|
|
59
|
+
label: 'Default For Device',
|
|
60
|
+
value: 'Default For Device'
|
|
61
|
+
}, {
|
|
62
|
+
label: 'Scale To Fit',
|
|
63
|
+
value: 'Scale To Fit'
|
|
64
|
+
}, {
|
|
65
|
+
label: 'Scale To 1024x768',
|
|
66
|
+
value: 'Scale To 1024x768'
|
|
67
|
+
}],
|
|
68
|
+
initialValue: 'Default For Device'
|
|
69
|
+
}));
|
|
70
|
+
|
|
71
|
+
const disableActions = handleCanceled<Actions[]>(await multiselect({
|
|
72
|
+
message: 'Select any actions you would like to disable globally',
|
|
73
|
+
required: false,
|
|
74
|
+
options: [{
|
|
75
|
+
label: 'History Buttons',
|
|
76
|
+
value: 'History Buttons'
|
|
77
|
+
}, {
|
|
78
|
+
label: 'Navigation Bar',
|
|
79
|
+
value: 'Navigation Bar'
|
|
80
|
+
}, {
|
|
81
|
+
label: 'Pinch to Exit',
|
|
82
|
+
value: 'Pinch to Exit'
|
|
83
|
+
}, {
|
|
84
|
+
label: 'Reactions',
|
|
85
|
+
value: 'Reactions'
|
|
86
|
+
}, {
|
|
87
|
+
label: 'Rotation Lock',
|
|
88
|
+
value: 'Rotation Lock'
|
|
89
|
+
}, {
|
|
90
|
+
label: 'Swipe',
|
|
91
|
+
value: 'Swipe'
|
|
92
|
+
}, {
|
|
93
|
+
label: 'Zoom',
|
|
94
|
+
value: 'Zoom'
|
|
95
|
+
}]
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
const crmTarget = handleCanceled<CrmTarget>(await select({
|
|
99
|
+
message: 'Select the Veeva platform version',
|
|
100
|
+
options: [{
|
|
101
|
+
label: 'Salesforce',
|
|
102
|
+
value: 'Salesforce'
|
|
103
|
+
}, {
|
|
104
|
+
label: 'Vault',
|
|
105
|
+
value: 'Vault'
|
|
106
|
+
}],
|
|
107
|
+
initialValue: 'Salesforce'
|
|
108
|
+
}));
|
|
109
|
+
|
|
110
|
+
const spin = spinner();
|
|
111
|
+
spin.start('Generating config file...');
|
|
112
|
+
await sleep(); // make it seem like we're doing something
|
|
113
|
+
|
|
114
|
+
const slides = [];
|
|
115
|
+
const slideIds = new Set<string>();
|
|
116
|
+
const pages = await readdir(resolvePath(rootDir, 'src/pages'));
|
|
117
|
+
for (const page of pages) {
|
|
118
|
+
if (page.endsWith('.astro')) {
|
|
119
|
+
const p = page.slice(0, -6);
|
|
120
|
+
let slideId = generateRandomId();
|
|
121
|
+
while (slideIds.has(slideId)) {
|
|
122
|
+
slideId = generateRandomId();
|
|
123
|
+
}
|
|
124
|
+
slideIds.add(slideId);
|
|
125
|
+
slides.push(`{
|
|
126
|
+
path: '${p}',
|
|
127
|
+
title: '${titleCase(p)}',
|
|
128
|
+
externalId: formatExtId('${slideId}')
|
|
129
|
+
}`)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let template = await readFile(join(__dirname, 'templates/config.ts.txt'), 'utf8');
|
|
134
|
+
template = template
|
|
135
|
+
.replace(/##EXTERNAL_ID##/g, `${cleanInput(product).substring(0, 3).toUpperCase()}_${generateRandomId()}`)
|
|
136
|
+
.replace(/##PRODUCT##/g, cleanInput(product))
|
|
137
|
+
.replace(/##PRESENTATION_NAME##/g, cleanInput(presentation))
|
|
138
|
+
.replace(/##PRESENTATION_ID##/g, cleanInput(presentationId))
|
|
139
|
+
.replace(/##DIMENSIONS_WIDTH##/g, width)
|
|
140
|
+
.replace(/##DIMENSIONS_HEIGHT##/g, height)
|
|
141
|
+
.replace(/##IOS_RESOLUTION##/g, iosResolution)
|
|
142
|
+
.replace(/##DISABLE_ACTIONS##/g, `[${disableActions.map((a) => { return `'${a}'`}).join(', ')}]`)
|
|
143
|
+
.replace(/##CRM_TARGET##/g, crmTarget)
|
|
144
|
+
.replace(/##SLIDES##/g, `[${slides.join(', ')}]`)
|
|
145
|
+
.replace(/##SLIDE_IDS##/g, [...slideIds].join());
|
|
146
|
+
await writeFile(resolvePath(rootDir, 'veeva-config.ts'), template);
|
|
147
|
+
|
|
148
|
+
await sleep(); // make it seem like we're doing something
|
|
149
|
+
spin.stop('Config file generated!');
|
|
150
|
+
|
|
151
|
+
outro('Happy coding!');
|
|
152
|
+
}
|
|
153
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { resolve as resolvePath } from 'node:path';
|
|
3
|
+
import { intro } from '@clack/prompts';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export default async function({ root }: { root?: string } = {}) {
|
|
7
|
+
const rootDir = root ?? process.cwd();
|
|
8
|
+
const exists = existsSync(resolvePath(rootDir, 'veeva-config.ts'));
|
|
9
|
+
|
|
10
|
+
console.log('');
|
|
11
|
+
intro('Veeva Configuration Manager');
|
|
12
|
+
|
|
13
|
+
if (!exists) {
|
|
14
|
+
const create = await import('./create.js');
|
|
15
|
+
await create.default(rootDir);
|
|
16
|
+
} /* else {
|
|
17
|
+
const update = await import('../../../archive/update');
|
|
18
|
+
await update.default();
|
|
19
|
+
} */
|
|
20
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { IVeevaClmBinderFields, IVeevaConfig, VeevaEnv } from "@inizioevoke/veeva-astroclm-core/types";
|
|
2
|
+
import { formatExtId as __formatExtId } from '@inizioevoke/veeva-astroclm-core/lib';
|
|
3
|
+
|
|
4
|
+
interface IVeevaConfigArgs {
|
|
5
|
+
veevaEnv?: VeevaEnv;
|
|
6
|
+
isTraining?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export default function ({ veevaEnv = 'client', isTraining = false }: IVeevaConfigArgs = {}): IVeevaConfig {
|
|
9
|
+
const isEvokeBuild = veevaEnv === 'evoke';
|
|
10
|
+
const EXT_ID = `##EXTERNAL_ID##${isTraining ? '_TRN' : ''}`;
|
|
11
|
+
const PRESENTATION_NAME = '##PRESENTATION_NAME##';
|
|
12
|
+
|
|
13
|
+
const formatExtId = (id: string) => {
|
|
14
|
+
return __formatExtId(EXT_ID, id);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const binderFields: IVeevaClmBinderFields = {
|
|
18
|
+
// language: isEvokeBuild ? undefined : 'English',
|
|
19
|
+
// crmOrg: isEvokeBuild ? undefined : { crmId: ['CRM_ORD_ID'] },
|
|
20
|
+
// crmProduct: isEvokeBuild ? undefined : { id: 'VEEVA_ID_FROM_URL' },
|
|
21
|
+
// crmDetailGroup: isEvokeBuild ? undefined : { id: 'VEEVA_ID_FROM_URL' },
|
|
22
|
+
// detailGroup: undefined
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
dimensions: { width: ##DIMENSIONS_WIDTH##, height: ##DIMENSIONS_HEIGHT## },
|
|
27
|
+
thumbnails: {
|
|
28
|
+
default: {
|
|
29
|
+
queryStringParams: {
|
|
30
|
+
screenshots: true
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
presentation: {
|
|
35
|
+
externalId: EXT_ID,
|
|
36
|
+
presentationId: `##PRESENTATION_ID##${isTraining ? '_trn' : ''}`,
|
|
37
|
+
product: '##PRODUCT##',
|
|
38
|
+
name: `${PRESENTATION_NAME}${isTraining ? ' [Training]' : ''}`,
|
|
39
|
+
country: 'United States',
|
|
40
|
+
isTraining,
|
|
41
|
+
crmTarget: '##CRM_TARGET##',
|
|
42
|
+
...binderFields,
|
|
43
|
+
|
|
44
|
+
shared: {
|
|
45
|
+
externalId: formatExtId('SHARED'),
|
|
46
|
+
...binderFields
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
slideSettings: {
|
|
50
|
+
mediaType: 'HTML',
|
|
51
|
+
disableActions: ##DISABLE_ACTIONS##,
|
|
52
|
+
iosResolution: '##IOS_RESOLUTION##',
|
|
53
|
+
...binderFields
|
|
54
|
+
},
|
|
55
|
+
slides: ##SLIDES##
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// SLIDE_IDS: [##SLIDE_IDS##]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { envField } from 'astro/config';
|
|
2
|
+
import {
|
|
3
|
+
BUILD_TARGETS, WEB_ENVS, VEEVA_ENVS, VEEVA_PLATFORMS, LOG_LEVELS, LOG_TIMES, LOG_OUTPUTS,
|
|
4
|
+
DEFAULT_BUILD_TARGET, DEFAULT_WEB_ENV, DEFAULT_VEEVA_ENV, DEFAULT_VEEVA_PLATFORM, DEFAULT_LOG_LEVEL, DEFAULT_LOG_TIME, DEFAULT_LOG_OUTPUT,
|
|
5
|
+
} from '../lib/const.js';
|
|
6
|
+
|
|
7
|
+
const envPublicOptional: {
|
|
8
|
+
context: 'client',
|
|
9
|
+
access: 'public',
|
|
10
|
+
optional: true
|
|
11
|
+
} = {
|
|
12
|
+
context: 'client',
|
|
13
|
+
access: 'public',
|
|
14
|
+
optional: true
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type BooleanField = ReturnType<typeof envField.boolean>;
|
|
18
|
+
type EnumField = ReturnType<typeof envField.enum>;
|
|
19
|
+
|
|
20
|
+
export type EnvFieldName = 'BUILD_TARGET' | 'WEB_ENV' | 'WEB_SCALING' | 'VEEVA_TRAINING' | 'VEEVA_ENV' | 'VEEVA_PLATFORM' | 'LOG_LEVEL' | 'LOG_TIME' | 'LOG_OUTPUT'
|
|
21
|
+
|
|
22
|
+
export const BUILD_TARGET: EnumField = envField.enum({ ...envPublicOptional, values: BUILD_TARGETS, default: DEFAULT_BUILD_TARGET });
|
|
23
|
+
export const WEB_ENV: EnumField = envField.enum({ ...envPublicOptional, values: WEB_ENVS, default: DEFAULT_WEB_ENV });
|
|
24
|
+
export const WEB_SCALING: BooleanField = envField.boolean({ ...envPublicOptional, default: false });
|
|
25
|
+
export const VEEVA_TRAINING: BooleanField = envField.boolean({ ...envPublicOptional, default: false });
|
|
26
|
+
export const VEEVA_ENV: EnumField = envField.enum({ ...envPublicOptional, values: VEEVA_ENVS, default: DEFAULT_VEEVA_ENV });
|
|
27
|
+
export const VEEVA_PLATFORM: EnumField = envField.enum({ ...envPublicOptional, values: VEEVA_PLATFORMS, default: DEFAULT_VEEVA_PLATFORM });
|
|
28
|
+
export const LOG_LEVEL: EnumField = envField.enum({ ...envPublicOptional, values: LOG_LEVELS, default: DEFAULT_LOG_LEVEL });
|
|
29
|
+
export const LOG_TIME: EnumField = envField.enum({ ...envPublicOptional, values: LOG_TIMES, default: DEFAULT_LOG_TIME });
|
|
30
|
+
export const LOG_OUTPUT: EnumField = envField.enum({ ...envPublicOptional, values: LOG_OUTPUTS, default: DEFAULT_LOG_OUTPUT});
|
|
31
|
+
|
|
32
|
+
const schema: Record<EnvFieldName, BooleanField | EnumField> = {
|
|
33
|
+
BUILD_TARGET,
|
|
34
|
+
WEB_ENV,
|
|
35
|
+
WEB_SCALING,
|
|
36
|
+
VEEVA_TRAINING,
|
|
37
|
+
VEEVA_ENV,
|
|
38
|
+
VEEVA_PLATFORM,
|
|
39
|
+
LOG_LEVEL,
|
|
40
|
+
LOG_TIME,
|
|
41
|
+
LOG_OUTPUT
|
|
42
|
+
};
|
|
43
|
+
export default schema;
|
package/src/lib/const.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BuildTarget, WebEnv, VeevaEnv, VeevaPlatform, LogLevel, LogTime, LogOutput } from '../types';
|
|
2
|
+
|
|
3
|
+
export const BUILD_TARGETS: BuildTarget[] = ['veeva', 'web'];
|
|
4
|
+
export const WEB_ENVS: WebEnv[] = ['development', 'production'];
|
|
5
|
+
export const VEEVA_ENVS: VeevaEnv[] = ['client', 'evoke'];
|
|
6
|
+
export const VEEVA_PLATFORMS: VeevaPlatform[] = ['salesforce', 'vault'];
|
|
7
|
+
export const LOG_LEVELS: LogLevel[] = ['disabled', 'error', 'debug'];
|
|
8
|
+
export const LOG_TIMES: LogTime[] = ['none', 'date', 'time', 'datetime', 'performance'];
|
|
9
|
+
export const LOG_OUTPUTS: LogOutput[] = ['console', 'screen'];
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_BUILD_TARGET: BuildTarget = 'web';
|
|
12
|
+
export const DEFAULT_WEB_ENV: WebEnv = 'development';
|
|
13
|
+
export const DEFAULT_VEEVA_ENV: VeevaEnv = 'client';
|
|
14
|
+
export const DEFAULT_VEEVA_PLATFORM: VeevaPlatform = 'salesforce';
|
|
15
|
+
export const DEFAULT_LOG_LEVEL: LogLevel = 'disabled';
|
|
16
|
+
export const DEFAULT_LOG_TIME: LogTime = 'time';
|
|
17
|
+
export const DEFAULT_LOG_OUTPUT: LogOutput = 'console';
|
package/src/lib/env.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { BuildTarget, VeevaEnv, VeevaPlatform } from '../types';
|
|
2
|
+
import * as Constants from './const.js';
|
|
3
|
+
import parseEnv from "./parse-env.js";
|
|
4
|
+
|
|
5
|
+
export interface Env {
|
|
6
|
+
BUILD_TARGET: BuildTarget;
|
|
7
|
+
VEEVA_ENV: VeevaEnv;
|
|
8
|
+
VEEVA_PLATFORM: VeevaPlatform;
|
|
9
|
+
VEEVA_TRAINING: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default async function loadEnv(mode?: string): Promise<Env> {
|
|
13
|
+
const env = await parseEnv({
|
|
14
|
+
BUILD_TARGET: Constants.BUILD_TARGETS,
|
|
15
|
+
VEEVA_ENV: Constants.VEEVA_ENVS,
|
|
16
|
+
VEEVA_PLATFORM: Constants.VEEVA_PLATFORMS,
|
|
17
|
+
VEEVA_TRAINING: 'boolean'
|
|
18
|
+
}, {
|
|
19
|
+
mode
|
|
20
|
+
});
|
|
21
|
+
env.BUILD_TARGET = env.BUILD_TARGET ?? Constants.DEFAULT_BUILD_TARGET,
|
|
22
|
+
env.VEEVA_ENV = env.VEEVA_ENV ?? Constants.DEFAULT_VEEVA_ENV,
|
|
23
|
+
env.VEEVA_PLATFORM = env.VEEVA_PLATFORM ?? Constants.DEFAULT_VEEVA_PLATFORM,
|
|
24
|
+
env.VEEVA_TRAINING = env.VEEVA_TRAINING ?? false
|
|
25
|
+
|
|
26
|
+
return env as Env;
|
|
27
|
+
}
|
package/src/lib/index.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const Style = Object.freeze({
|
|
2
|
+
Background: {
|
|
3
|
+
Black: '\x1b[40m',
|
|
4
|
+
Red: '\x1b[41m',
|
|
5
|
+
Green: '\x1b[42m',
|
|
6
|
+
Yellow: '\x1b[43m',
|
|
7
|
+
Blue: '\x1b[44m',
|
|
8
|
+
Magenta: '\x1b[45m',
|
|
9
|
+
Cyan: '\x1b[46m',
|
|
10
|
+
White: '\x1b[47m',
|
|
11
|
+
Gray: '\x1b[100m',
|
|
12
|
+
BrightRed: '\x1b[101m',
|
|
13
|
+
BrightGreen: '\x1b[102m',
|
|
14
|
+
BrightYellow: '\x1b[103m',
|
|
15
|
+
BrightBlue: '\x1b[104m',
|
|
16
|
+
BrightMagenta: '\x1b[105m',
|
|
17
|
+
BrightCyan: '\x1b[106m',
|
|
18
|
+
BrightWhite: '\x1b[107m'
|
|
19
|
+
},
|
|
20
|
+
Foreground: {
|
|
21
|
+
Black: '\x1b[30m',
|
|
22
|
+
Red: '\x1b[31m',
|
|
23
|
+
Green: '\x1b[32m',
|
|
24
|
+
Yellow: '\x1b[33m',
|
|
25
|
+
Blue: '\x1b[34m',
|
|
26
|
+
Magenta: '\x1b[35m',
|
|
27
|
+
Cyan: '\x1b[36m',
|
|
28
|
+
White: '\x1b[37m',
|
|
29
|
+
Gray: '\x1b[90m',
|
|
30
|
+
BrightRed: '\x1b[91m',
|
|
31
|
+
BrightGreen: '\x1b[92m',
|
|
32
|
+
BrightYellow: '\x1b[93m',
|
|
33
|
+
BrightBlue: '\x1b[94m',
|
|
34
|
+
BrightMagenta: '\x1b[95m',
|
|
35
|
+
BrightCyan: '\x1b[96m',
|
|
36
|
+
BrightWhite: '\x1b[97m'
|
|
37
|
+
},
|
|
38
|
+
Bold: '\x1b[1m',
|
|
39
|
+
Inverse: '\x1b[7m',
|
|
40
|
+
Reset: '\x1b[0m',
|
|
41
|
+
Underscore: '\x1b[4m'
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
interface LogMessage {
|
|
45
|
+
task?: string;
|
|
46
|
+
message: string;
|
|
47
|
+
data?: any;
|
|
48
|
+
style?: string;
|
|
49
|
+
}
|
|
50
|
+
function _log({ task, message, data, style }: LogMessage) {
|
|
51
|
+
const now = new Date();
|
|
52
|
+
const ts = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
|
|
53
|
+
|
|
54
|
+
const msg = [`${Style.Foreground.Gray}${ts}${Style.Reset}`];
|
|
55
|
+
if (task) {
|
|
56
|
+
msg.push(`${Style.Foreground.Cyan}[${task}]${Style.Reset}`);
|
|
57
|
+
}
|
|
58
|
+
msg.push(`${style ?? Style.Reset}${message}${Style.Reset}`);
|
|
59
|
+
if (data) {
|
|
60
|
+
msg.push(`${Style.Foreground.Yellow}${data}${Style.Reset}`)
|
|
61
|
+
}
|
|
62
|
+
console.log(msg.join(' '));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function info(message: string) { // task: string, message: string, data?: any) {
|
|
66
|
+
_log({ message, style: Style.Foreground.White });
|
|
67
|
+
// _log({ task, message, data, style: Style.Foreground.White });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function warn(message: string) { // task: string, message: string, data?: any) {
|
|
71
|
+
_log({ message, style: Style.Foreground.Yellow });
|
|
72
|
+
// _log({ task, message, data, style: Style.Foreground.Yellow });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function error(message: string) { // task: string, message: string, data?: any) {
|
|
76
|
+
_log({ message, style: Style.Foreground.Red });
|
|
77
|
+
// _log({ task, message, data, style: Style.Foreground.Red });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default {
|
|
81
|
+
info,
|
|
82
|
+
warn,
|
|
83
|
+
error
|
|
84
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses process.argv or any string[] into an object with the given schema
|
|
3
|
+
* Properties should be prefixed with `--`
|
|
4
|
+
* Values should come immediately after the property and be separated by a space or `=`
|
|
5
|
+
* Type `array` can only be a array of `boolean`, `number`, or `string` types
|
|
6
|
+
* Type `boolean` will be `true` if present and not followed by `=[VALUE]`
|
|
7
|
+
* Type `object` property values can only by of `boolean`, `number`, or `string` types
|
|
8
|
+
* Schema entries may also be string‑literal arrays, which are treated as enum
|
|
9
|
+
* definitions; the resulting value will be typed as the union of the literals.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const argv = parseArgv({
|
|
13
|
+
* a: 'array',
|
|
14
|
+
* b: 'boolean',
|
|
15
|
+
* b2: 'boolean',
|
|
16
|
+
* f: 'float',
|
|
17
|
+
* n: 'number',
|
|
18
|
+
* o: 'object',
|
|
19
|
+
* s: 'string',
|
|
20
|
+
* mode: ['dev','prod'] as const // enum-style union
|
|
21
|
+
* })
|
|
22
|
+
*
|
|
23
|
+
* > npm run build -- --a [one,2,false] --b --b2=false --f 3.14 --n 123 --o {one:1,nope:false,hello:world} --s mystring --mode dev
|
|
24
|
+
*
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
// primitive types that may be specified in the schema. enum definitions are
|
|
28
|
+
// provided using string literal arrays directly on the schema entries.
|
|
29
|
+
type Type = 'array' | 'boolean' | 'float' | 'number' | 'object' | 'string';
|
|
30
|
+
|
|
31
|
+
type Schema<S extends Record<string, Type | readonly string[]>> = {
|
|
32
|
+
[K in keyof S]:
|
|
33
|
+
S[K] extends readonly (infer U)[] ? U | undefined
|
|
34
|
+
: S[K] extends 'array' ? any[] | undefined
|
|
35
|
+
: S[K] extends 'boolean' ? boolean | undefined
|
|
36
|
+
: S[K] extends 'float' ? number | undefined
|
|
37
|
+
: S[K] extends 'number' ? number | undefined
|
|
38
|
+
: S[K] extends 'object' ? Record<string, any> | undefined
|
|
39
|
+
: S[K] extends 'string' ? string | undefined
|
|
40
|
+
: string | undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default function<S extends Record<string, Type | readonly string[]>>(schema: S, argv: string[] = process.argv.slice(2)): Schema<S> {
|
|
44
|
+
const data: Schema<S> = {} as Schema<S>;
|
|
45
|
+
const args = argv.slice()
|
|
46
|
+
.map((arg) => { return arg?.trim(); })
|
|
47
|
+
.filter(arg => typeof arg == 'string' && arg !== '');
|
|
48
|
+
for (let i=0; i<args.length; i++) {
|
|
49
|
+
if (args[i].startsWith('--')) {
|
|
50
|
+
const arg = args[i].slice(2);
|
|
51
|
+
if (arg.indexOf('=') !== -1) {
|
|
52
|
+
let [key, value] = arg.split('=') as [string, any];
|
|
53
|
+
if (schema[key]) {
|
|
54
|
+
value = parseValue(schema[key], value);
|
|
55
|
+
data[key as keyof Schema<S>] = value as any;
|
|
56
|
+
}
|
|
57
|
+
} else if (schema[arg] === 'boolean') {
|
|
58
|
+
data[arg as keyof Schema<S>] = true as any;
|
|
59
|
+
} else if (schema[arg]) {
|
|
60
|
+
let value: any = true;
|
|
61
|
+
if (i + 1 < args.length && !args[i+1].startsWith('--')) {
|
|
62
|
+
value = parseValue(schema[arg], args[++i]);
|
|
63
|
+
}
|
|
64
|
+
data[arg as keyof Schema<S>] = value as any;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return data;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parseValue(type: Type | readonly string[], value: string): any {
|
|
73
|
+
if (typeof value != 'string') {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
if (Array.isArray(type)) {
|
|
77
|
+
return (type.includes(value) ? value : value) as any;
|
|
78
|
+
}
|
|
79
|
+
switch (type) {
|
|
80
|
+
case 'array': {
|
|
81
|
+
if (value.startsWith('[')) {
|
|
82
|
+
value = value.substring(1);
|
|
83
|
+
}
|
|
84
|
+
if (value.endsWith(']')) {
|
|
85
|
+
value = value.slice(0, -1);
|
|
86
|
+
}
|
|
87
|
+
const items = value.split(',')
|
|
88
|
+
.map((v) => {
|
|
89
|
+
v = v.trim();
|
|
90
|
+
if (/(true|false|\d+(?:\.\d+)?)/i.test(v)) {
|
|
91
|
+
return JSON.parse(v);
|
|
92
|
+
} else {
|
|
93
|
+
return v;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return items;
|
|
98
|
+
}
|
|
99
|
+
case 'boolean':
|
|
100
|
+
return value.toLowerCase() === 'true' || value === '1';
|
|
101
|
+
case 'float':
|
|
102
|
+
return parseFloat(value);
|
|
103
|
+
case 'number':
|
|
104
|
+
return parseInt(value, 10);
|
|
105
|
+
case 'object':
|
|
106
|
+
if (value.startsWith('{') && value.endsWith('}')) {
|
|
107
|
+
const obj = value.substring(1, value.length - 1)
|
|
108
|
+
.split(',')
|
|
109
|
+
.filter(p => p.trim() !== '')
|
|
110
|
+
.map((pair) => {
|
|
111
|
+
let [key, val] = pair.split(':').map(s => s.trim());
|
|
112
|
+
if (/(true|false|\d+(?:\.\d+)?)/i.test(val)) {
|
|
113
|
+
val = JSON.parse(val);
|
|
114
|
+
}
|
|
115
|
+
return [key,val];
|
|
116
|
+
});
|
|
117
|
+
return Object.fromEntries(obj);
|
|
118
|
+
} else {
|
|
119
|
+
return {};
|
|
120
|
+
}
|
|
121
|
+
case 'string':
|
|
122
|
+
default:
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a .env file into an object with the given schema
|
|
3
|
+
* Values should come immediately after the property and be separated by `=`
|
|
4
|
+
* Values can be quoted or unquoted
|
|
5
|
+
* Type `array` can only be a array of `boolean`, `number`, or `string` types
|
|
6
|
+
* Type `object` property values can only by of `boolean`, `number`, or `string` types
|
|
7
|
+
* Schema entries may also be string‑literal arrays, which are treated as enum
|
|
8
|
+
* definitions; the resulting value will be typed as the union of the literals.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* import parseEnv from './parse-env';
|
|
12
|
+
*
|
|
13
|
+
* const env = parseEnv({
|
|
14
|
+
* a: 'array',
|
|
15
|
+
* b: 'boolean',
|
|
16
|
+
* b2: 'boolean',
|
|
17
|
+
* f: 'float',
|
|
18
|
+
* n: 'number',
|
|
19
|
+
* o: 'object',
|
|
20
|
+
* s: 'string',
|
|
21
|
+
* mode: ['dev','prod'] as const // enum-style union
|
|
22
|
+
* })
|
|
23
|
+
*
|
|
24
|
+
*
|
|
25
|
+
* // .env
|
|
26
|
+
* a="[1,2,3]"
|
|
27
|
+
* b=false
|
|
28
|
+
* b2="true"
|
|
29
|
+
* f="1.23"
|
|
30
|
+
* n=234
|
|
31
|
+
* o={"test":1}
|
|
32
|
+
* s="testing 1 2 3"
|
|
33
|
+
* mode=prod
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import { existsSync } from "node:fs";
|
|
37
|
+
import { readFile } from "node:fs/promises";
|
|
38
|
+
import { join } from 'node:path';
|
|
39
|
+
|
|
40
|
+
// primitive types that can be declared in the schema
|
|
41
|
+
// (enum definitions are represented by string literal arrays in the schema itself)
|
|
42
|
+
export type EnvPropertyType = 'array' | 'boolean' | 'float' | 'number' | 'object' | 'string';
|
|
43
|
+
|
|
44
|
+
// When the schema entry is an array of literals we treat it as an enum definition
|
|
45
|
+
// and the resulting value is the union of those literals.
|
|
46
|
+
// Otherwise we fall back to the primitive mapping that was already present.
|
|
47
|
+
|
|
48
|
+
export type EnvSchema<S extends Record<string, EnvPropertyType | readonly string[]> = Record<string, any>> = {
|
|
49
|
+
[K in keyof S]:
|
|
50
|
+
S[K] extends readonly (infer U)[] ? U | undefined
|
|
51
|
+
: S[K] extends 'array' ? any[] | undefined
|
|
52
|
+
: S[K] extends 'boolean' ? boolean | undefined
|
|
53
|
+
: S[K] extends 'float' ? number | undefined
|
|
54
|
+
: S[K] extends 'number' ? number | undefined
|
|
55
|
+
: S[K] extends 'object' ? Record<string, any> | undefined
|
|
56
|
+
: S[K] extends 'string' ? string | undefined
|
|
57
|
+
: string | undefined
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// parse reads a .env file according to the given schema. In addition to
|
|
61
|
+
// the primitive types listed above the schema may use a string-literal
|
|
62
|
+
// array to describe an enum; the returned object will then have the property
|
|
63
|
+
// typed as the union of those literals.
|
|
64
|
+
async function parseEnv<S extends Record<string, EnvPropertyType | readonly string[]>>(schema: S, { mode, cwd, filePath, all = false }: { mode?: string, cwd?: string, filePath?: string, all?: boolean } = {}): Promise<EnvSchema<S>> {
|
|
65
|
+
const env: EnvSchema<S> = {} as EnvSchema<S>;
|
|
66
|
+
|
|
67
|
+
const fp = filePath ? cwd ? join(cwd, filePath) : filePath : join(cwd ?? process.cwd(), `.env${mode ? `.${mode}` : ''}`);
|
|
68
|
+
if (existsSync(fp)) {
|
|
69
|
+
const data: Record<string, string> = {};
|
|
70
|
+
const content = await readFile(fp, 'utf8');
|
|
71
|
+
const lines = content.split(/\r?\n/).filter(l => l.trim() !== '');
|
|
72
|
+
for (const line of lines) {
|
|
73
|
+
const [key, value] = line.split('=');
|
|
74
|
+
data[key.trim()] = value.trim().match(/['"']?([^'"]*)['"]?/)?.[1].trim() ?? '';
|
|
75
|
+
}
|
|
76
|
+
for (const [key, value] of Object.entries(data)) {
|
|
77
|
+
if (schema[key]) {
|
|
78
|
+
env[key as keyof EnvSchema<S>] = parseValue(schema[key], value);
|
|
79
|
+
} else if (all) {
|
|
80
|
+
env[key as keyof EnvSchema<S>] = value as any;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return env;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
function parseValue(type: EnvPropertyType | readonly string[], value: string): any {
|
|
89
|
+
if (typeof value != 'string') {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
// enums are defined as string literal arrays in the schema
|
|
93
|
+
if (Array.isArray(type)) {
|
|
94
|
+
// return the value directly; optionally we could validate, but at runtime
|
|
95
|
+
// we simply cast back to one of the allowed values (union type ensures
|
|
96
|
+
// callers only use valid strings at compile time if schema is const).
|
|
97
|
+
return (type.includes(value) ? value : value) as any;
|
|
98
|
+
}
|
|
99
|
+
switch (type) {
|
|
100
|
+
case 'array': {
|
|
101
|
+
if (value.startsWith('[')) {
|
|
102
|
+
value = value.substring(1);
|
|
103
|
+
}
|
|
104
|
+
if (value.endsWith(']')) {
|
|
105
|
+
value = value.slice(0, -1);
|
|
106
|
+
}
|
|
107
|
+
const items = value.split(',')
|
|
108
|
+
.map((v) => {
|
|
109
|
+
v = v.trim();
|
|
110
|
+
if (/(true|false|\d+(?:\.\d+)?)/i.test(v)) {
|
|
111
|
+
return JSON.parse(v);
|
|
112
|
+
} else {
|
|
113
|
+
return v;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return items;
|
|
118
|
+
}
|
|
119
|
+
case 'boolean':
|
|
120
|
+
return value.toLowerCase() === 'true' || value === '1';
|
|
121
|
+
case 'float':
|
|
122
|
+
return parseFloat(value);
|
|
123
|
+
case 'number':
|
|
124
|
+
return parseInt(value, 10);
|
|
125
|
+
case 'object':
|
|
126
|
+
if (value.startsWith('{') && value.endsWith('}')) {
|
|
127
|
+
const obj = value.substring(1, value.length - 1)
|
|
128
|
+
.split(',')
|
|
129
|
+
.filter(p => p.trim() !== '')
|
|
130
|
+
.map((pair) => {
|
|
131
|
+
let [key, val] = pair.split(':').map(s => s.trim());
|
|
132
|
+
if (/(true|false|\d+(?:\.\d+)?)/i.test(val)) {
|
|
133
|
+
val = JSON.parse(val);
|
|
134
|
+
}
|
|
135
|
+
return [key,val];
|
|
136
|
+
});
|
|
137
|
+
return Object.fromEntries(obj);
|
|
138
|
+
} else {
|
|
139
|
+
return {};
|
|
140
|
+
}
|
|
141
|
+
case 'string':
|
|
142
|
+
default:
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export default parseEnv;
|