@vercel/ruby 2.3.1 → 2.3.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/index.js +11 -1
- package/package.json +4 -3
- package/vc_init.rb +12 -2
- package/vc_init_dev.rb +21 -3
- package/vc_utils.rb +101 -0
package/dist/index.js
CHANGED
|
@@ -7232,10 +7232,14 @@ function createDevRubyShim(workPath, entrypoint) {
|
|
|
7232
7232
|
const vercelRubyDir = (0, import_path2.join)(workPath, ".vercel", "ruby");
|
|
7233
7233
|
(0, import_fs.mkdirSync)(vercelRubyDir, { recursive: true });
|
|
7234
7234
|
const shimPath = (0, import_path2.join)(vercelRubyDir, `vc_init_dev.rb`);
|
|
7235
|
+
const utilsPath = (0, import_path2.join)(vercelRubyDir, "vc__utils__ruby.rb");
|
|
7235
7236
|
const templatePath = (0, import_path2.join)(__dirname, "..", "vc_init_dev.rb");
|
|
7237
|
+
const utilsTemplatePath = (0, import_path2.join)(__dirname, "..", "vc_utils.rb");
|
|
7236
7238
|
const template = (0, import_fs.readFileSync)(templatePath, "utf8");
|
|
7239
|
+
const utilsTemplate = (0, import_fs.readFileSync)(utilsTemplatePath, "utf8");
|
|
7237
7240
|
const shimSource = template.replace(/__VC_DEV_ENTRYPOINT__/g, entrypoint);
|
|
7238
7241
|
(0, import_fs.writeFileSync)(shimPath, shimSource, "utf8");
|
|
7242
|
+
(0, import_fs.writeFileSync)(utilsPath, utilsTemplate, "utf8");
|
|
7239
7243
|
(0, import_build_utils2.debug)(`Prepared Ruby dev shim at ${shimPath}`);
|
|
7240
7244
|
return shimPath;
|
|
7241
7245
|
} catch (err) {
|
|
@@ -7616,6 +7620,8 @@ var build = async ({
|
|
|
7616
7620
|
}
|
|
7617
7621
|
const originalRbPath = (0, import_path3.join)(__dirname, "..", "vc_init.rb");
|
|
7618
7622
|
const originalHandlerRbContents = await (0, import_fs_extra.readFile)(originalRbPath, "utf8");
|
|
7623
|
+
const originalUtilsRbPath = (0, import_path3.join)(__dirname, "..", "vc_utils.rb");
|
|
7624
|
+
const originalUtilsRbContents = await (0, import_fs_extra.readFile)(originalUtilsRbPath, "utf8");
|
|
7619
7625
|
(0, import_build_utils3.debug)("entrypoint is", entrypoint);
|
|
7620
7626
|
const userHandlerFilePath = entrypoint.replace(/\.rb$/, "");
|
|
7621
7627
|
const nowHandlerRbContents = originalHandlerRbContents.replace(
|
|
@@ -7623,6 +7629,7 @@ var build = async ({
|
|
|
7623
7629
|
userHandlerFilePath
|
|
7624
7630
|
);
|
|
7625
7631
|
const handlerRbFilename = "vc__handler__ruby";
|
|
7632
|
+
const utilsRbFilename = "vc__utils__ruby.rb";
|
|
7626
7633
|
const predefinedExcludes = [
|
|
7627
7634
|
".git/**",
|
|
7628
7635
|
".gitignore",
|
|
@@ -7640,6 +7647,9 @@ var build = async ({
|
|
|
7640
7647
|
outputFiles[`${handlerRbFilename}.rb`] = new import_build_utils3.FileBlob({
|
|
7641
7648
|
data: nowHandlerRbContents
|
|
7642
7649
|
});
|
|
7650
|
+
outputFiles[utilsRbFilename] = new import_build_utils3.FileBlob({
|
|
7651
|
+
data: originalUtilsRbContents
|
|
7652
|
+
});
|
|
7643
7653
|
if (config && (config.includeFiles || config.excludeFiles)) {
|
|
7644
7654
|
const includedPaths = await matchPaths(config.includeFiles, workPath);
|
|
7645
7655
|
const excludedPaths = await matchPaths(
|
|
@@ -7650,7 +7660,7 @@ var build = async ({
|
|
|
7650
7660
|
if (includedPaths.includes(excludedPaths[i])) {
|
|
7651
7661
|
continue;
|
|
7652
7662
|
}
|
|
7653
|
-
if (excludedPaths[i] === `${handlerRbFilename}.rb`) {
|
|
7663
|
+
if (excludedPaths[i] === `${handlerRbFilename}.rb` || excludedPaths[i] === utilsRbFilename) {
|
|
7654
7664
|
continue;
|
|
7655
7665
|
}
|
|
7656
7666
|
if (excludedPaths[i].startsWith(vendorPath)) {
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/ruby",
|
|
3
3
|
"author": "Nathan Cahill <nathan@nathancahill.com>",
|
|
4
|
-
"version": "2.3.
|
|
4
|
+
"version": "2.3.2",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index",
|
|
7
7
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist",
|
|
10
10
|
"vc_init.rb",
|
|
11
|
-
"vc_init_dev.rb"
|
|
11
|
+
"vc_init_dev.rb",
|
|
12
|
+
"vc_utils.rb"
|
|
12
13
|
],
|
|
13
14
|
"repository": {
|
|
14
15
|
"type": "git",
|
|
@@ -24,7 +25,7 @@
|
|
|
24
25
|
"jest-junit": "16.0.0",
|
|
25
26
|
"semver": "6.3.1",
|
|
26
27
|
"which": "3.0.0",
|
|
27
|
-
"@vercel/build-utils": "13.
|
|
28
|
+
"@vercel/build-utils": "13.5.0"
|
|
28
29
|
},
|
|
29
30
|
"scripts": {
|
|
30
31
|
"build": "node ../../utils/build-builder.mjs",
|
package/vc_init.rb
CHANGED
|
@@ -3,6 +3,7 @@ require 'webrick'
|
|
|
3
3
|
require 'net/http'
|
|
4
4
|
require 'base64'
|
|
5
5
|
require 'json'
|
|
6
|
+
require_relative 'vc__utils__ruby'
|
|
6
7
|
|
|
7
8
|
$entrypoint = '__VC_HANDLER_FILENAME'
|
|
8
9
|
|
|
@@ -10,13 +11,17 @@ ENV['RAILS_ENV'] ||= 'production'
|
|
|
10
11
|
ENV['RACK_ENV'] ||= 'production'
|
|
11
12
|
ENV['RAILS_LOG_TO_STDOUT'] ||= '1'
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
$service_route_prefix = resolve_service_route_prefix
|
|
16
|
+
|
|
17
|
+
def rack_handler(httpMethod, path, body, headers, script_name = '')
|
|
14
18
|
require 'rack'
|
|
15
19
|
|
|
16
20
|
app, _ = Rack::Builder.parse_file($entrypoint)
|
|
17
21
|
server = Rack::MockRequest.new app
|
|
18
22
|
|
|
19
23
|
env = headers.transform_keys { |k| k.split('-').join('_').prepend('HTTP_').upcase }
|
|
24
|
+
env['SCRIPT_NAME'] = script_name unless script_name.empty?
|
|
20
25
|
res = server.request(httpMethod, path, env.merge({ :input => body }))
|
|
21
26
|
|
|
22
27
|
{
|
|
@@ -96,8 +101,13 @@ def vc__handler(event:, context:)
|
|
|
96
101
|
body = Base64.decode64(body)
|
|
97
102
|
end
|
|
98
103
|
|
|
104
|
+
path, script_name = apply_service_route_prefix_to_target(
|
|
105
|
+
path,
|
|
106
|
+
$service_route_prefix
|
|
107
|
+
)
|
|
108
|
+
|
|
99
109
|
if $entrypoint.end_with? '.ru'
|
|
100
|
-
return rack_handler(httpMethod, path, body, headers)
|
|
110
|
+
return rack_handler(httpMethod, path, body, headers, script_name)
|
|
101
111
|
end
|
|
102
112
|
|
|
103
113
|
return webrick_handler(httpMethod, path, body, headers)
|
package/vc_init_dev.rb
CHANGED
|
@@ -10,12 +10,14 @@ require 'rack'
|
|
|
10
10
|
require 'rack/handler/webrick'
|
|
11
11
|
require 'webrick'
|
|
12
12
|
require 'socket'
|
|
13
|
+
require_relative 'vc__utils__ruby'
|
|
13
14
|
|
|
14
15
|
$stdout.sync = true
|
|
15
16
|
$stderr.sync = true
|
|
16
17
|
|
|
17
18
|
USER_ENTRYPOINT = "__VC_DEV_ENTRYPOINT__"
|
|
18
19
|
PUBLIC_DIR = 'public'
|
|
20
|
+
SERVICE_ROUTE_PREFIX = resolve_service_route_prefix
|
|
19
21
|
|
|
20
22
|
def build_user_app
|
|
21
23
|
if USER_ENTRYPOINT.end_with?('.ru')
|
|
@@ -36,17 +38,33 @@ class StaticThenApp
|
|
|
36
38
|
end
|
|
37
39
|
|
|
38
40
|
def call(env)
|
|
39
|
-
|
|
41
|
+
effective_env = env.dup
|
|
42
|
+
req_path, matched_prefix = strip_service_route_prefix(
|
|
43
|
+
effective_env['PATH_INFO'] || '/',
|
|
44
|
+
SERVICE_ROUTE_PREFIX
|
|
45
|
+
)
|
|
46
|
+
effective_env['PATH_INFO'] = req_path
|
|
47
|
+
|
|
48
|
+
unless matched_prefix.empty?
|
|
49
|
+
script_name = effective_env['SCRIPT_NAME'] || ''
|
|
50
|
+
if !script_name.empty? && script_name != '/'
|
|
51
|
+
script_name = script_name.sub(%r{/+\z}, '')
|
|
52
|
+
else
|
|
53
|
+
script_name = ''
|
|
54
|
+
end
|
|
55
|
+
effective_env['SCRIPT_NAME'] = "#{script_name}#{matched_prefix}"
|
|
56
|
+
end
|
|
57
|
+
|
|
40
58
|
# Normalize path and guard against traversal
|
|
41
59
|
safe = req_path.sub(/^\//, '')
|
|
42
60
|
full = File.expand_path(safe, @base)
|
|
43
61
|
|
|
44
62
|
if full.start_with?(@base + File::SEPARATOR) && File.file?(full)
|
|
45
63
|
# Delegate to Rack::File which handles HEAD/GET correctly
|
|
46
|
-
return @file_server.call(
|
|
64
|
+
return @file_server.call(effective_env)
|
|
47
65
|
end
|
|
48
66
|
|
|
49
|
-
@app.call(
|
|
67
|
+
@app.call(effective_env)
|
|
50
68
|
end
|
|
51
69
|
end
|
|
52
70
|
|
package/vc_utils.rb
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
|
|
3
|
+
# Normalize a configured service prefix into canonical mount-path form.
|
|
4
|
+
#
|
|
5
|
+
# Returns an empty string when unset/blank/root ("/"), otherwise:
|
|
6
|
+
# - always starts with "/"
|
|
7
|
+
# - has no trailing slash
|
|
8
|
+
def normalize_service_route_prefix(raw_prefix)
|
|
9
|
+
return '' if raw_prefix.nil?
|
|
10
|
+
|
|
11
|
+
prefix = raw_prefix.strip
|
|
12
|
+
return '' if prefix.empty?
|
|
13
|
+
|
|
14
|
+
prefix = "/#{prefix}" unless prefix.start_with?('/')
|
|
15
|
+
|
|
16
|
+
if prefix != '/'
|
|
17
|
+
prefix = prefix.sub(%r{/+\z}, '')
|
|
18
|
+
prefix = '/' if prefix.empty?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
prefix == '/' ? '' : prefix
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Return whether service-prefix stripping is explicitly enabled.
|
|
25
|
+
#
|
|
26
|
+
# Stripping is gated behind a dedicated env var so manually configured
|
|
27
|
+
# route prefixes can be preserved by default.
|
|
28
|
+
def service_route_prefix_strip_enabled?
|
|
29
|
+
raw = ENV['VERCEL_SERVICE_ROUTE_PREFIX_STRIP']
|
|
30
|
+
return false if raw.nil? || raw.empty?
|
|
31
|
+
|
|
32
|
+
%w[1 true].include?(raw.downcase)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def resolve_service_route_prefix
|
|
36
|
+
return '' unless service_route_prefix_strip_enabled?
|
|
37
|
+
|
|
38
|
+
normalize_service_route_prefix(ENV['VERCEL_SERVICE_ROUTE_PREFIX'])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Split an HTTP request-target into [path, query].
|
|
42
|
+
#
|
|
43
|
+
# Supports:
|
|
44
|
+
# - origin-form: "/a/b?x=1"
|
|
45
|
+
# - absolute-form: "https://example.com/a/b?x=1"
|
|
46
|
+
# - asterisk-form: "*"
|
|
47
|
+
def split_request_target(target)
|
|
48
|
+
return ['/', ''] if target.nil? || target.empty?
|
|
49
|
+
|
|
50
|
+
begin
|
|
51
|
+
parsed = URI.parse(target)
|
|
52
|
+
if parsed.scheme && parsed.host
|
|
53
|
+
path = parsed.path.nil? || parsed.path.empty? ? '/' : parsed.path
|
|
54
|
+
return [path, parsed.query.to_s]
|
|
55
|
+
end
|
|
56
|
+
rescue URI::InvalidURIError
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
return ['*', ''] if target == '*'
|
|
60
|
+
|
|
61
|
+
path, query = target.split('?', 2)
|
|
62
|
+
path = '/' if path.nil? || path.empty?
|
|
63
|
+
path = "/#{path}" unless path.start_with?('/')
|
|
64
|
+
[path, query.to_s]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Strip the configured service route prefix from a request path.
|
|
68
|
+
#
|
|
69
|
+
# Returns:
|
|
70
|
+
# - stripped path passed to the user app
|
|
71
|
+
# - matched mount prefix (empty when no prefix matched)
|
|
72
|
+
def strip_service_route_prefix(path, prefix)
|
|
73
|
+
return [path, ''] if path == '*'
|
|
74
|
+
|
|
75
|
+
normalized_path = path.nil? || path.empty? ? '/' : path
|
|
76
|
+
normalized_path = "/#{normalized_path}" unless normalized_path.start_with?('/')
|
|
77
|
+
|
|
78
|
+
normalized_prefix = prefix.to_s
|
|
79
|
+
return [normalized_path, ''] if normalized_prefix.empty?
|
|
80
|
+
|
|
81
|
+
return ['/', normalized_prefix] if normalized_path == normalized_prefix
|
|
82
|
+
|
|
83
|
+
if normalized_path.start_with?("#{normalized_prefix}/")
|
|
84
|
+
stripped = normalized_path[normalized_prefix.length..]
|
|
85
|
+
return [stripped.nil? || stripped.empty? ? '/' : stripped, normalized_prefix]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
[normalized_path, '']
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Apply service-prefix stripping to a full request-target.
|
|
92
|
+
#
|
|
93
|
+
# Returns:
|
|
94
|
+
# - updated request-target (path + optional query)
|
|
95
|
+
# - matched mount prefix for Rack SCRIPT_NAME (or "")
|
|
96
|
+
def apply_service_route_prefix_to_target(target, prefix)
|
|
97
|
+
path, query = split_request_target(target)
|
|
98
|
+
path, script_name = strip_service_route_prefix(path, prefix)
|
|
99
|
+
updated = query.empty? ? path : "#{path}?#{query}"
|
|
100
|
+
[updated, script_name]
|
|
101
|
+
end
|