@tremho/mist-lift 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/LICENSE +21 -0
- package/README.md +37 -0
- package/build/commands/actions/initQuestions.js +136 -0
- package/build/commands/actions/initQuestions.js.map +1 -0
- package/build/commands/actions/makePackageJson.js +2 -0
- package/build/commands/actions/makePackageJson.js.map +1 -0
- package/build/commands/actions/setupPackageJson.js +68 -0
- package/build/commands/actions/setupPackageJson.js.map +1 -0
- package/build/commands/build.js +191 -0
- package/build/commands/build.js.map +1 -0
- package/build/commands/builtin/ApiDocMaker.js +72 -0
- package/build/commands/builtin/ApiDocMaker.js.map +1 -0
- package/build/commands/builtin/BuiltInHandler.js +62 -0
- package/build/commands/builtin/BuiltInHandler.js.map +1 -0
- package/build/commands/builtin/DeployBuiltInZip.js +57 -0
- package/build/commands/builtin/DeployBuiltInZip.js.map +1 -0
- package/build/commands/builtin/StageWebrootZip.js +70 -0
- package/build/commands/builtin/StageWebrootZip.js.map +1 -0
- package/build/commands/builtin/prebuilt-zips/API.zip +0 -0
- package/build/commands/builtin/prebuilt-zips/FileServe.zip +0 -0
- package/build/commands/builtin/prebuilt-zips/Webroot.zip +0 -0
- package/build/commands/create.js +75 -0
- package/build/commands/create.js.map +1 -0
- package/build/commands/deploy.js +187 -0
- package/build/commands/deploy.js.map +1 -0
- package/build/commands/doctor.js +137 -0
- package/build/commands/doctor.js.map +1 -0
- package/build/commands/help.js +205 -0
- package/build/commands/help.js.map +1 -0
- package/build/commands/init.js +92 -0
- package/build/commands/init.js.map +1 -0
- package/build/commands/package.js +249 -0
- package/build/commands/package.js.map +1 -0
- package/build/commands/publish.js +344 -0
- package/build/commands/publish.js.map +1 -0
- package/build/commands/settings.js +95 -0
- package/build/commands/settings.js.map +1 -0
- package/build/commands/start.js +66 -0
- package/build/commands/start.js.map +1 -0
- package/build/commands/test.js +52 -0
- package/build/commands/test.js.map +1 -0
- package/build/commands/user.js +20 -0
- package/build/commands/user.js.map +1 -0
- package/build/expressRoutes/all.js +129 -0
- package/build/expressRoutes/all.js.map +1 -0
- package/build/expressRoutes/api.js +22 -0
- package/build/expressRoutes/api.js.map +1 -0
- package/build/expressRoutes/functionBinder.js +191 -0
- package/build/expressRoutes/functionBinder.js.map +1 -0
- package/build/lib/CaseUtils.js +57 -0
- package/build/lib/CaseUtils.js.map +1 -0
- package/build/lib/DirectoryUtils.js +37 -0
- package/build/lib/DirectoryUtils.js.map +1 -0
- package/build/lib/LiftConfig.js +83 -0
- package/build/lib/LiftConfig.js.map +1 -0
- package/build/lib/LiftVersion.js +117 -0
- package/build/lib/LiftVersion.js.map +1 -0
- package/build/lib/Tests/fileCompare.test.js +55 -0
- package/build/lib/Tests/fileCompare.test.js.map +1 -0
- package/build/lib/askQuestion.js +41 -0
- package/build/lib/askQuestion.js.map +1 -0
- package/build/lib/executeCommand.js +45 -0
- package/build/lib/executeCommand.js.map +1 -0
- package/build/lib/fileCompare.js +48 -0
- package/build/lib/fileCompare.js.map +1 -0
- package/build/lib/openAPI/ApiBuildCollector.js +58 -0
- package/build/lib/openAPI/ApiBuildCollector.js.map +1 -0
- package/build/lib/openAPI/WebrootFileSupport.js +44 -0
- package/build/lib/openAPI/WebrootFileSupport.js.map +1 -0
- package/build/lib/openAPI/openApiConstruction.js +203 -0
- package/build/lib/openAPI/openApiConstruction.js.map +1 -0
- package/build/lib/pathResolve.js +27 -0
- package/build/lib/pathResolve.js.map +1 -0
- package/build/lib/utils.js +76 -0
- package/build/lib/utils.js.map +1 -0
- package/build/lift.js +124 -0
- package/build/lift.js.map +1 -0
- package/package.json +69 -0
- package/src/commands/actions/initQuestions.ts +131 -0
- package/src/commands/actions/makePackageJson.ts +0 -0
- package/src/commands/actions/setupPackageJson.ts +36 -0
- package/src/commands/build.ts +165 -0
- package/src/commands/builtin/ApiDocMaker.ts +70 -0
- package/src/commands/builtin/BuiltInHandler.ts +51 -0
- package/src/commands/builtin/DeployBuiltInZip.ts +27 -0
- package/src/commands/builtin/StageWebrootZip.ts +39 -0
- package/src/commands/builtin/prebuilt-zips/API.zip +0 -0
- package/src/commands/builtin/prebuilt-zips/FileServe.zip +0 -0
- package/src/commands/builtin/prebuilt-zips/Webroot.zip +0 -0
- package/src/commands/create.ts +55 -0
- package/src/commands/deploy.ts +171 -0
- package/src/commands/doctor.ts +102 -0
- package/src/commands/help.ts +171 -0
- package/src/commands/init.ts +62 -0
- package/src/commands/package.ts +228 -0
- package/src/commands/publish.ts +350 -0
- package/src/commands/settings.ts +76 -0
- package/src/commands/start.ts +46 -0
- package/src/commands/test.ts +38 -0
- package/src/commands/user.ts +20 -0
- package/src/expressRoutes/all.ts +104 -0
- package/src/expressRoutes/api.ts +24 -0
- package/src/expressRoutes/functionBinder.ts +169 -0
- package/src/lib/CaseUtils.ts +68 -0
- package/src/lib/DirectoryUtils.ts +36 -0
- package/src/lib/LiftConfig.ts +83 -0
- package/src/lib/LiftVersion.ts +95 -0
- package/src/lib/Tests/dir1/file.1 +0 -0
- package/src/lib/Tests/dir1/file.2 +0 -0
- package/src/lib/Tests/dir2/file.1 +0 -0
- package/src/lib/Tests/fileCompare.test.ts +34 -0
- package/src/lib/askQuestion.ts +18 -0
- package/src/lib/executeCommand.ts +38 -0
- package/src/lib/fileCompare.ts +46 -0
- package/src/lib/openAPI/ApiBuildCollector.ts +47 -0
- package/src/lib/openAPI/WebrootFileSupport.ts +21 -0
- package/src/lib/openAPI/openApiConstruction.ts +202 -0
- package/src/lib/pathResolve.ts +32 -0
- package/src/lib/utils.ts +45 -0
- package/src/lift.ts +82 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
|
|
4
|
+
const clearModule = require('clear-module')
|
|
5
|
+
|
|
6
|
+
import * as ac from "ansi-colors"
|
|
7
|
+
import {resolvePaths} from "../lib/pathResolve";
|
|
8
|
+
import {GetWebrootServePaths} from "../lib/openAPI/WebrootFileSupport"
|
|
9
|
+
import {doBuildAsync} from "../commands/build"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
import express from 'express'
|
|
13
|
+
import {gatherFunctionDefinitions} from "../lib/openAPI/ApiBuildCollector";
|
|
14
|
+
import {buildOpenApi} from "../lib/openAPI/openApiConstruction";
|
|
15
|
+
const router = express.Router();
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
export function functionBinder() {
|
|
19
|
+
const defs = gatherFunctionDefinitions();
|
|
20
|
+
buildOpenApi(defs).then(() => { // creates apidoc.yaml for when /api is run
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
const projectPaths = resolvePaths();
|
|
24
|
+
|
|
25
|
+
for (let def of defs) {
|
|
26
|
+
const {name, pathMap, allowedMethods} = def;
|
|
27
|
+
const methods = allowedMethods.split(',');
|
|
28
|
+
for (let method of methods) {
|
|
29
|
+
method = method.trim().toLowerCase();
|
|
30
|
+
let rpath = path.join(projectPaths.buildPath, 'functions', name, 'src', 'main.js')
|
|
31
|
+
clearModule(rpath);
|
|
32
|
+
const {start} = require(rpath);
|
|
33
|
+
|
|
34
|
+
let entryRoot = pathMap;
|
|
35
|
+
let n = entryRoot.indexOf("/{");
|
|
36
|
+
if (n !== -1) entryRoot = entryRoot.substring(0, n) + "/*";
|
|
37
|
+
|
|
38
|
+
const callNoBody = (pathMap: string, req: any, res: any) => {
|
|
39
|
+
const event = requestToEvent(pathMap, req)
|
|
40
|
+
Promise.resolve(start(event, null, null)).then(respOut => {
|
|
41
|
+
handleResponse(res, respOut);
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
const callWithBody = (pathMap: string, req: any, res: any) => {
|
|
45
|
+
const event = requestToEvent(pathMap, req)
|
|
46
|
+
event.body = req.body
|
|
47
|
+
Promise.resolve(start(event, null, null)).then(respOut => {
|
|
48
|
+
handleResponse(res, respOut);
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (method === 'get') {
|
|
53
|
+
router.get(entryRoot, (req, res) => callNoBody(pathMap, req, res))
|
|
54
|
+
} else if (method === 'post') {
|
|
55
|
+
router.get(entryRoot, (req, res) => callWithBody(pathMap, req, res))
|
|
56
|
+
} else if (method === 'put') {
|
|
57
|
+
router.put(entryRoot, (req, res) => callWithBody(pathMap, req, res))
|
|
58
|
+
} else if (method === 'patch') {
|
|
59
|
+
router.patch(entryRoot, (req, res) => callWithBody(pathMap, req, res))
|
|
60
|
+
} else if (method === 'delete') {
|
|
61
|
+
router.delete(entryRoot, (req, res) => callNoBody(pathMap, req, res))
|
|
62
|
+
} else {
|
|
63
|
+
console.log(ac.red.bold("Cannot map method ") + ac.blue.bold(method))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function requestToEvent(template:string, req:any) {
|
|
72
|
+
let path = req.originalUrl
|
|
73
|
+
const qi = path.indexOf('?');
|
|
74
|
+
if(qi !== -1) path = path.substring(0,qi);
|
|
75
|
+
let ptci = path.indexOf("://")+3;
|
|
76
|
+
let ei = path.indexOf("/", ptci);
|
|
77
|
+
let host = path.substring(0, ei);
|
|
78
|
+
if(ptci < 3) {
|
|
79
|
+
host = req.headers?.origin;
|
|
80
|
+
if(!host) {
|
|
81
|
+
host = req.headers?.referer ?? "";
|
|
82
|
+
let ptci = path.indexOf("://")+3;
|
|
83
|
+
let ei = path.indexOf("/", ptci);
|
|
84
|
+
host = ptci > 3 ? path.substring(0, ei) : "";
|
|
85
|
+
}
|
|
86
|
+
if(!host) {
|
|
87
|
+
// todo: http or https?
|
|
88
|
+
host = "http://"+req.headers?.host ?? "";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
var cookies:any = {};
|
|
92
|
+
var cookieString = req.headers?.cookie ?? "";
|
|
93
|
+
var crumbs = cookieString.split(';')
|
|
94
|
+
for(let c of crumbs) {
|
|
95
|
+
const pair:string[] = c.split('=');
|
|
96
|
+
if(pair.length === 2) cookies[pair[0]] = pair[1]
|
|
97
|
+
}
|
|
98
|
+
const parameters:any = {}
|
|
99
|
+
const tslots = template.split('/');
|
|
100
|
+
const pslots = path.split('/');
|
|
101
|
+
for(let i = 0; i< tslots.length; i++) {
|
|
102
|
+
const brknm = (tslots[i]??"").trim();
|
|
103
|
+
if(brknm.charAt(0) === '{') {
|
|
104
|
+
const pn = brknm.substring(1, brknm.length - 1);
|
|
105
|
+
parameters[pn] = (pslots[i]??"").trim();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for(let p of Object.getOwnPropertyNames(req.query)) {
|
|
109
|
+
parameters[p] = req.query[p]
|
|
110
|
+
}
|
|
111
|
+
const eventOut:any = {
|
|
112
|
+
requestContext: req,
|
|
113
|
+
request: {
|
|
114
|
+
originalUrl: host + req.originalUrl,
|
|
115
|
+
headers: req.headers
|
|
116
|
+
},
|
|
117
|
+
cookies,
|
|
118
|
+
parameters
|
|
119
|
+
}
|
|
120
|
+
return eventOut;
|
|
121
|
+
}
|
|
122
|
+
function handleResponse(res:any, resp:any)
|
|
123
|
+
{
|
|
124
|
+
// console.log(">>>>>>>> Handling response >>>>>>>>>")
|
|
125
|
+
|
|
126
|
+
if(resp) {
|
|
127
|
+
if (resp.cookies !== undefined) {
|
|
128
|
+
// console.log("--- see cookies", resp.cookies)
|
|
129
|
+
var cookies:any = []
|
|
130
|
+
if(Array.isArray(resp.cookies)) {
|
|
131
|
+
cookies = resp.cookies
|
|
132
|
+
} else {
|
|
133
|
+
var age = resp.cookies.expireSeconds ?? 60; // 1 minute
|
|
134
|
+
delete resp.expireSeconds;
|
|
135
|
+
Object.getOwnPropertyNames(resp.cookies).forEach(name => {
|
|
136
|
+
var value = resp.cookies[name];
|
|
137
|
+
cookies.push(`${name}=${value}; Max-Age=${age}; `);
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
// console.log("cookies being set", cookies)
|
|
141
|
+
res.setHeader("set-cookie", cookies);
|
|
142
|
+
delete resp.cookies;
|
|
143
|
+
}
|
|
144
|
+
if (resp.headers !== undefined) {
|
|
145
|
+
// if (resp.statusCode === 301) ClogTrace("Redirecting...");
|
|
146
|
+
for (var hdr of Object.getOwnPropertyNames(resp.headers)) {
|
|
147
|
+
// ClogTrace("Setting header ", hdr, resp.headers[hdr]);
|
|
148
|
+
res.setHeader(hdr, resp.headers[hdr])
|
|
149
|
+
}
|
|
150
|
+
delete resp.headers;
|
|
151
|
+
// console.log("past setting headers");
|
|
152
|
+
}
|
|
153
|
+
if (resp.statusCode !== undefined) {
|
|
154
|
+
res.statusCode = resp.statusCode;
|
|
155
|
+
delete resp.statusCode
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (resp.contentType !== undefined) {
|
|
159
|
+
res.setHeader("Content-Type", resp.contentType);
|
|
160
|
+
delete resp.contentType
|
|
161
|
+
}
|
|
162
|
+
if(resp.body) resp = resp.body;
|
|
163
|
+
}
|
|
164
|
+
// console.log("headers to be sent", res.getHeaders())
|
|
165
|
+
// console.log("-- Sending response", resp)
|
|
166
|
+
res.send(resp);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export default router
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
// separate a case-joined string into a string of space-separated words
|
|
3
|
+
export function separated(
|
|
4
|
+
instr:string
|
|
5
|
+
):string
|
|
6
|
+
{
|
|
7
|
+
let outstr = '';
|
|
8
|
+
for(let i=0; i<instr.length; i++) {
|
|
9
|
+
let c = instr.charAt(i);
|
|
10
|
+
if(!isAlphaNum(c) || isUpperCase(c)) {
|
|
11
|
+
outstr += ' ';
|
|
12
|
+
}
|
|
13
|
+
outstr += c.toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return outstr.trim()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isUpperCase(instr:string) {
|
|
20
|
+
return instr?.toUpperCase() === instr
|
|
21
|
+
}
|
|
22
|
+
function isAlphaNum(char:string) {
|
|
23
|
+
let alnum = false
|
|
24
|
+
const cc = char.toUpperCase().charCodeAt(0)
|
|
25
|
+
if(cc >= 'A'.charCodeAt(0) && cc <= 'Z'.charCodeAt(0)) alnum = true
|
|
26
|
+
if(cc >= '0'.charCodeAt(0) && cc <= '9'.charCodeAt(0)) alnum = true
|
|
27
|
+
return alnum
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function camelCase(
|
|
31
|
+
instr:string
|
|
32
|
+
):string
|
|
33
|
+
{
|
|
34
|
+
const seps = separated(instr).split(' ')
|
|
35
|
+
for(let i=1; i<seps.length; i++) {
|
|
36
|
+
seps[i] = seps[i].charAt(0).toUpperCase()+seps[i].substring(1)
|
|
37
|
+
}
|
|
38
|
+
return seps.join('')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function pascalCase(
|
|
42
|
+
instr:string
|
|
43
|
+
):string
|
|
44
|
+
{
|
|
45
|
+
const seps = separated(instr).split(' ')
|
|
46
|
+
for(let i=0; i<seps.length; i++) {
|
|
47
|
+
seps[i] = seps[i].charAt(0).toUpperCase()+seps[i].substring(1)
|
|
48
|
+
}
|
|
49
|
+
return seps.join('')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function snakeCase(
|
|
53
|
+
instr:string
|
|
54
|
+
):string
|
|
55
|
+
{
|
|
56
|
+
return dashCase(instr, "_")
|
|
57
|
+
}
|
|
58
|
+
export function dashCase(
|
|
59
|
+
instr:string,
|
|
60
|
+
dashChar: string = '-',
|
|
61
|
+
allCaps: boolean = false
|
|
62
|
+
):string
|
|
63
|
+
{
|
|
64
|
+
const seps = separated(instr).split(' ')
|
|
65
|
+
let out = seps.join(dashChar)
|
|
66
|
+
if(allCaps) out = out.toUpperCase();
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import {Stats} from 'fs'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
|
|
6
|
+
// Callback definition for recurseDirectory
|
|
7
|
+
export interface RecurseCB {
|
|
8
|
+
(filepath: string, stats: Stats): boolean|void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Recurse a directory, calling back to a 'for-each' callback for all files in tree
|
|
12
|
+
export function recurseDirectory(dirpath:string, callback:RecurseCB) {
|
|
13
|
+
fs.readdirSync(dirpath).forEach((file:string) => {
|
|
14
|
+
const fpath = path.join(dirpath, file)
|
|
15
|
+
const stat = fs.lstatSync(fpath)
|
|
16
|
+
if(callback && !callback(fpath, stat)) {
|
|
17
|
+
if (stat.isDirectory()) {
|
|
18
|
+
recurseDirectory(fpath, callback)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// find the latest mod time for matching files in this folder tree
|
|
25
|
+
export function latestModification(dirpath:string, match:string) {
|
|
26
|
+
let latestTime = new Date(0);
|
|
27
|
+
if(fs.existsSync(dirpath)) {
|
|
28
|
+
recurseDirectory(dirpath, (filepath: string, stats: Stats) => {
|
|
29
|
+
let basefile = filepath.substring(filepath.lastIndexOf('/') + 1)
|
|
30
|
+
if (basefile.match(match)) {
|
|
31
|
+
if (stats.mtime > latestTime) latestTime = stats.mtime
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
return latestTime
|
|
36
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// configuration for setting cloud provider credentials
|
|
2
|
+
// may expand later for other MistLift settings/preferences
|
|
3
|
+
|
|
4
|
+
import path from "path";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import AWS, {fromIni} from '@aws-sdk/credential-providers';
|
|
7
|
+
import {NodeJsRuntimeStreamingBlobTypes} from "@smithy/types";
|
|
8
|
+
|
|
9
|
+
let s_liftConfigLoaded:LiftConfig|null = null;
|
|
10
|
+
|
|
11
|
+
// Defines the structure of the .mistlft json file
|
|
12
|
+
// All relevant configuration for the cloud host goes here
|
|
13
|
+
export class LiftConfig {
|
|
14
|
+
public cloudHost: string = "AWS" // all we support now
|
|
15
|
+
public awsIniProfile?: string
|
|
16
|
+
public awsPreferredRegion?:string
|
|
17
|
+
public awsNodeRuntime?: RuntimeType
|
|
18
|
+
public awsServiceRoleARN?:string
|
|
19
|
+
}
|
|
20
|
+
export type RuntimeType = "nodejs" | "nodejs4.3" | "nodejs6.10" | "nodejs8.10" | "nodejs10.x" | "nodejs12.x" | "nodejs14.x" | "nodejs16.x" | "java8" | "java8.al2" | "java11" | "python2.7" | "python3.6" | "python3.7" | "python3.8" | "python3.9" | "dotnetcore1.0" | "dotnetcore2.0" | "dotnetcore2.1" | "dotnetcore3.1" | "dotnet6" | "dotnet8" | "nodejs4.3-edge" | "go1.x" | "ruby2.5" | "ruby2.7" | "provided" | "provided.al2" | "nodejs18.x" | "python3.10" | "java17" | "ruby3.2" | "ruby3.3" | "python3.11" | "nodejs20.x" | "provided.al2023" | "python3.12" | "java21";
|
|
21
|
+
|
|
22
|
+
// Available for general use because, why not?
|
|
23
|
+
export function getUserHome() {
|
|
24
|
+
return process.env.HOME ?? process.env.HOMEPATH ?? process.env.USERPROFILE ?? "~";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function LoadLiftConfig() : LiftConfig|null
|
|
28
|
+
{
|
|
29
|
+
if(!s_liftConfigLoaded) {
|
|
30
|
+
|
|
31
|
+
const mistlift = path.join(getUserHome(), ".mistlift");
|
|
32
|
+
let configJson = "{}";
|
|
33
|
+
if (fs.existsSync(mistlift)) {
|
|
34
|
+
try {
|
|
35
|
+
configJson = fs.readFileSync(mistlift).toString();
|
|
36
|
+
s_liftConfigLoaded = JSON.parse(configJson);
|
|
37
|
+
} catch (e: any) {
|
|
38
|
+
//
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return s_liftConfigLoaded
|
|
43
|
+
}
|
|
44
|
+
export function resetLiftConfig()
|
|
45
|
+
{
|
|
46
|
+
s_liftConfigLoaded = null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function areSettingsAvailable():boolean {
|
|
50
|
+
const mistlift = path.join(getUserHome(), ".mistlift");
|
|
51
|
+
return fs.existsSync(mistlift);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function getSettings() : LiftConfig {
|
|
55
|
+
if (s_liftConfigLoaded == null) LoadLiftConfig();
|
|
56
|
+
return s_liftConfigLoaded as LiftConfig
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Return the credentials required for AWS based upon our config options.
|
|
61
|
+
*
|
|
62
|
+
* We will use the standard .aws config ini file, looked up by profile or default
|
|
63
|
+
* Prior attempts to have it do more than this failed, and resulted in a default profile choice anyway.
|
|
64
|
+
*/
|
|
65
|
+
export function getAWSCredentials() : any
|
|
66
|
+
{
|
|
67
|
+
let config = LoadLiftConfig()
|
|
68
|
+
if(!config) {
|
|
69
|
+
// console.error("No .mistlift configuration found - using AWS default profile");
|
|
70
|
+
config = { cloudHost: "AWS", awsIniProfile: "default" }
|
|
71
|
+
}
|
|
72
|
+
if (config.cloudHost?.toUpperCase() !== "AWS")
|
|
73
|
+
{
|
|
74
|
+
throw Error("HostType Not Supported");
|
|
75
|
+
}
|
|
76
|
+
let credentials = {};
|
|
77
|
+
const profile = config.awsIniProfile ?? "default"
|
|
78
|
+
if(profile) {
|
|
79
|
+
credentials = fromIni({profile})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return credentials;
|
|
83
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
import * as fs from 'fs'
|
|
4
|
+
import {resolvePaths} from "./pathResolve";
|
|
5
|
+
|
|
6
|
+
export class VersionInfo
|
|
7
|
+
{
|
|
8
|
+
public major:number = 0
|
|
9
|
+
public minor:number = 0
|
|
10
|
+
public revision:number = 0
|
|
11
|
+
public suffix:string = "";
|
|
12
|
+
|
|
13
|
+
constructor(vstr:string) {
|
|
14
|
+
this.parse(vstr);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public toString = () => {
|
|
18
|
+
return `${this.major}.${this.minor}.${this.revision}`+(this.suffix? "-"+this.suffix : "")
|
|
19
|
+
}
|
|
20
|
+
public parse = (vstr:string) => {
|
|
21
|
+
let sn = vstr.indexOf('-');
|
|
22
|
+
let suffix = ""
|
|
23
|
+
if(sn !== -1) {
|
|
24
|
+
suffix = vstr.substring(sn+1);
|
|
25
|
+
vstr = vstr.substring(0,sn);
|
|
26
|
+
}
|
|
27
|
+
let parts = vstr.split('.');
|
|
28
|
+
while(parts.length < 3) parts.push("0");
|
|
29
|
+
this.major = parseInt(parts[0])
|
|
30
|
+
this.minor = parseInt(parts[1]);
|
|
31
|
+
this.revision = parseInt(parts[2]);
|
|
32
|
+
if(!isFinite(this.major)) this.major = 0;
|
|
33
|
+
if(!isFinite(this.minor)) this.minor = 0;
|
|
34
|
+
if(!isFinite(this.revision)) this.revision = 0;
|
|
35
|
+
this.suffix = suffix;
|
|
36
|
+
}
|
|
37
|
+
public equals(other: VersionInfo | string): boolean
|
|
38
|
+
{
|
|
39
|
+
// Note: not really a valid SemVer comparison
|
|
40
|
+
if(typeof other === 'string') other = new VersionInfo(other);
|
|
41
|
+
return this.major === other.major
|
|
42
|
+
&& this.minor === other.minor
|
|
43
|
+
&& this.revision === other.revision
|
|
44
|
+
}
|
|
45
|
+
public isGreaterThan(other:VersionInfo | string): boolean
|
|
46
|
+
{
|
|
47
|
+
// Note: not really a valid SemVer comparison
|
|
48
|
+
if(typeof other === 'string') other = new VersionInfo(other);
|
|
49
|
+
if(this.major > other.major) return true;
|
|
50
|
+
if(this.major < other.major) return false;
|
|
51
|
+
if(this.minor > other.minor) return true;
|
|
52
|
+
if(this.minor < other.minor) return false;
|
|
53
|
+
return (this.revision > other.revision);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// read version from Lift's package.json
|
|
58
|
+
export function getLiftVersion()
|
|
59
|
+
{
|
|
60
|
+
let pkg = path.join(__dirname, '..', '..', 'package.json');
|
|
61
|
+
return readPackageVersion(pkg);
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
// read version from project's package.json
|
|
65
|
+
export function getProjectVersion()
|
|
66
|
+
{
|
|
67
|
+
const projectPaths = resolvePaths();
|
|
68
|
+
return readPackageVersion(projectPaths.packagePath)
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function getProjectName()
|
|
73
|
+
{
|
|
74
|
+
const projectPaths = resolvePaths();
|
|
75
|
+
let pkgJson:{name?:string} = {}
|
|
76
|
+
try {
|
|
77
|
+
pkgJson = JSON.parse(fs.readFileSync(projectPaths.packagePath).toString())
|
|
78
|
+
}
|
|
79
|
+
catch(e) {}
|
|
80
|
+
return pkgJson.name;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function readPackageVersion(pkgPath:string):VersionInfo
|
|
84
|
+
{
|
|
85
|
+
let pkgJson:{version?:string} = {}
|
|
86
|
+
try {
|
|
87
|
+
pkgJson = JSON.parse(fs.readFileSync(pkgPath).toString())
|
|
88
|
+
}
|
|
89
|
+
catch(e) {}
|
|
90
|
+
|
|
91
|
+
return new VersionInfo(pkgJson.version ?? "")
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
import Tap from "tap"
|
|
3
|
+
|
|
4
|
+
import * as path from 'path'
|
|
5
|
+
import {isNewer, isNewerFile} from "../fileCompare";
|
|
6
|
+
|
|
7
|
+
function test(t:any) {
|
|
8
|
+
|
|
9
|
+
// ./dir1/file.1 551 < ./dir2/file.1 552
|
|
10
|
+
// ./dir1/file.2 553 > ./dir2/file.1 552
|
|
11
|
+
// ./dir1 553 > ./dir2 552
|
|
12
|
+
// .dir1 (.1) 552 < ./dir2 552
|
|
13
|
+
|
|
14
|
+
const dir1 = path.join(__dirname, "dir1");
|
|
15
|
+
const dir2 = path.join(__dirname, "dir2");
|
|
16
|
+
const dir1File1 = path.join(dir1, "file.1");
|
|
17
|
+
const dir1File2 = path.join(dir1, "file.2");
|
|
18
|
+
const dir2File1 = path.join(dir2, "file.1");
|
|
19
|
+
|
|
20
|
+
let newer = isNewerFile(dir1File1, dir2File1)
|
|
21
|
+
t.equal(newer, false, "./dir1/file.1 < ./dir2/file.1")
|
|
22
|
+
newer = isNewerFile(dir1File2, dir2File1)
|
|
23
|
+
t.equal(newer, true, "./dir1/file.2 > ./dir2/file.1")
|
|
24
|
+
newer = isNewerFile(dir1, dir2);
|
|
25
|
+
t.ok(newer, "dir1 > dir2");
|
|
26
|
+
newer = isNewerFile(dir1, dir2, ".1");
|
|
27
|
+
t.ok(!newer, "./dir1 (.1) < ./dir2")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
t.end()
|
|
31
|
+
}
|
|
32
|
+
Tap.test('IsNewer', t => {
|
|
33
|
+
test(t)
|
|
34
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** handles interaction for asking a question */
|
|
2
|
+
|
|
3
|
+
import * as ac from "ansi-colors"
|
|
4
|
+
const readlineSync = require("readline-sync")
|
|
5
|
+
|
|
6
|
+
export function ask(
|
|
7
|
+
desc:string, // describes the context
|
|
8
|
+
query:string, // asks the actual question
|
|
9
|
+
def:string // default answer
|
|
10
|
+
|
|
11
|
+
) :string // answer provided
|
|
12
|
+
{
|
|
13
|
+
console.log(ac.dim.blue.italic(desc))
|
|
14
|
+
let answer = readlineSync.question(ac.bold.green(query) + ac.dim.grey(` [${def}] `)+ '? ')
|
|
15
|
+
if(!answer) answer = def
|
|
16
|
+
return answer
|
|
17
|
+
}
|
|
18
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
import {exec} from 'child_process'
|
|
3
|
+
import * as path from 'path'
|
|
4
|
+
|
|
5
|
+
export function executeCommand(cmd:string, args:any[], cwd = '', consolePass = false, env:any = {}):Promise<any> {
|
|
6
|
+
const out = {
|
|
7
|
+
stdStr: '',
|
|
8
|
+
errStr: '',
|
|
9
|
+
retcode: 0
|
|
10
|
+
}
|
|
11
|
+
return new Promise(resolve => {
|
|
12
|
+
let cmdstr = cmd + ' ' + args.join(' ')
|
|
13
|
+
// console.log('executing ', cmdstr, 'at', cwd)
|
|
14
|
+
const opts = {
|
|
15
|
+
cwd:cwd,
|
|
16
|
+
env: Object.assign(env, process.env)
|
|
17
|
+
}
|
|
18
|
+
const proc = exec(cmdstr, opts)
|
|
19
|
+
if(proc.stdout) proc.stdout.on('data', data => {
|
|
20
|
+
out.stdStr += data.toString()
|
|
21
|
+
if(consolePass) process.stdout.write(data.toString())
|
|
22
|
+
})
|
|
23
|
+
if(proc.stderr) proc.stderr.on('data', data => {
|
|
24
|
+
out.errStr += data.toString()
|
|
25
|
+
if(consolePass) process.stdout.write(data.toString())
|
|
26
|
+
})
|
|
27
|
+
proc.on('error', error => {
|
|
28
|
+
console.error(error)
|
|
29
|
+
if(!out.errStr) out.errStr = error.message
|
|
30
|
+
out.retcode = -1
|
|
31
|
+
resolve(out)
|
|
32
|
+
})
|
|
33
|
+
proc.on('close', code => {
|
|
34
|
+
out.retcode = code === null ? -1 : code
|
|
35
|
+
resolve(out)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import {recurseDirectory} from "./DirectoryUtils";
|
|
4
|
+
|
|
5
|
+
// true if filepath > mapped file in outDir (i.e. .ts > .js)
|
|
6
|
+
export function isNewer(filepath:string, outDir:string, mappingObj:any = {".ts": ".js"})
|
|
7
|
+
{
|
|
8
|
+
let basename = path.basename(filepath);
|
|
9
|
+
let ext = path.extname(filepath)
|
|
10
|
+
basename = basename.substring(0, basename.lastIndexOf(ext));
|
|
11
|
+
let newExt = "";
|
|
12
|
+
|
|
13
|
+
if(mappingObj[ext]) newExt = mappingObj[ext];
|
|
14
|
+
|
|
15
|
+
let outfile = path.join(outDir, basename+newExt);
|
|
16
|
+
return isNewerFile(filepath, outfile);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// true if filepath is newer than destpath
|
|
20
|
+
export function isNewerFile(filepath:string, destpath:string, srcFilter:string = "", dstFilter:string = "")
|
|
21
|
+
{
|
|
22
|
+
const sstat = fs.statSync(filepath);
|
|
23
|
+
const srcTime = sstat.isDirectory() ? latestInDirectory(filepath, srcFilter) : fs.statSync(filepath).mtime;
|
|
24
|
+
let destTime = new Date(0);
|
|
25
|
+
if(fs.existsSync((destpath))) {
|
|
26
|
+
const dstat = fs.statSync(destpath);
|
|
27
|
+
destTime = dstat.isDirectory() ? latestInDirectory(destpath, dstFilter) : fs.statSync(destpath).mtime;
|
|
28
|
+
}
|
|
29
|
+
return (srcTime >= destTime)
|
|
30
|
+
}
|
|
31
|
+
function latestInDirectory(dirPath:string, extFilter:string = "")
|
|
32
|
+
{
|
|
33
|
+
let newestTime = new Date(0);
|
|
34
|
+
recurseDirectory(dirPath, (filepath, stats) => {
|
|
35
|
+
if(stats.isFile()) {
|
|
36
|
+
const ext = path.extname(filepath);
|
|
37
|
+
if(!extFilter || ext === extFilter) {
|
|
38
|
+
if(stats.mtime > newestTime) {
|
|
39
|
+
newestTime = stats.mtime
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
return newestTime;
|
|
45
|
+
|
|
46
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Part 1 of two steps to build openApi docs.
|
|
3
|
+
* This reads values from our "definitions.json" format and creates a single array that step 2 will turn into
|
|
4
|
+
* openApi specifications.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'fs'
|
|
7
|
+
import path from 'path'
|
|
8
|
+
import {recurseDirectory} from "../DirectoryUtils";
|
|
9
|
+
import {resolvePaths} from "../pathResolve";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export function gatherFunctionDefinitions():any[] {
|
|
13
|
+
const defs:any = []
|
|
14
|
+
try {
|
|
15
|
+
const projectPaths = resolvePaths();
|
|
16
|
+
if(!projectPaths.verified) return [];
|
|
17
|
+
const funcNames: string[] = [];
|
|
18
|
+
if(!fs.existsSync(projectPaths.functionPath)) return [];
|
|
19
|
+
let firstDepth = 0;
|
|
20
|
+
recurseDirectory(projectPaths.functionPath, (filepath, stats) => {
|
|
21
|
+
if (stats.isDirectory()) {
|
|
22
|
+
let depth = filepath.split(path.sep).length
|
|
23
|
+
if(!firstDepth) firstDepth = depth
|
|
24
|
+
if(firstDepth == depth) {
|
|
25
|
+
funcNames.push(path.basename(filepath));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
if(projectPaths.functionPath.indexOf('functions') !== -1 ) {
|
|
30
|
+
for (let name of funcNames) {
|
|
31
|
+
let defPath = path.join(projectPaths.functionPath, name, 'src', 'definition.json')
|
|
32
|
+
if (fs.existsSync(defPath)) {
|
|
33
|
+
const content = fs.readFileSync(defPath).toString()
|
|
34
|
+
let buildPath = path.join(projectPaths.buildPath, 'functions', name, 'src', 'definition.json')
|
|
35
|
+
// fs.writeFileSync(buildPath, content); // use this opportunity to copy to build folder before we use it.
|
|
36
|
+
defs.push(JSON.parse(content))
|
|
37
|
+
} else {
|
|
38
|
+
console.error(`Definition file not found at ${defPath}`);
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} catch(e: any) {
|
|
44
|
+
console.error("Exception in ApiBuildCollector", e)
|
|
45
|
+
}
|
|
46
|
+
return defs;
|
|
47
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
import {resolvePaths} from "../pathResolve";
|
|
4
|
+
import {recurseDirectory} from "../DirectoryUtils";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export function GetWebrootServePaths(): string[]
|
|
8
|
+
{
|
|
9
|
+
const projectPaths = resolvePaths();
|
|
10
|
+
const webroot = path.join(projectPaths.basePath, "webroot")
|
|
11
|
+
|
|
12
|
+
const list:string[] = [''];
|
|
13
|
+
recurseDirectory(webroot, (filepath, stats) => {
|
|
14
|
+
if(stats.isDirectory())
|
|
15
|
+
{
|
|
16
|
+
let relpath = filepath.substring(webroot.length);
|
|
17
|
+
if(list.indexOf(relpath) === -1) list.push(relpath);
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
return list;
|
|
21
|
+
}
|