cloud-run-functions 0.1.3 → 0.2.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/dist/chunk-MOB3GBJW.js +109 -0
- package/dist/chunk-NINUPPZA.js +122 -0
- package/dist/chunk-PFVLROGP.js +29 -0
- package/dist/chunk-SPF3AMMV.js +82 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +11 -1
- package/dist/main.d.ts +1 -2
- package/dist/main.js +69 -6
- package/dist/targets/build.d.ts +2 -0
- package/dist/targets/build.js +53 -0
- package/dist/targets/dev.js +27 -172
- package/dist/tools/build.d.ts +30 -1
- package/dist/tools/build.js +8 -0
- package/dist/tools/dev.d.ts +1 -0
- package/dist/tools/preview.d.ts +16 -0
- package/dist/tools/preview.js +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
dedent
|
|
3
|
+
} from "./chunk-SPF3AMMV.js";
|
|
4
|
+
|
|
5
|
+
// src/common/functionFilter.ts
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
function getFunctionFilter(options) {
|
|
8
|
+
const functionGlobs = [];
|
|
9
|
+
const functionSuffixes = /* @__PURE__ */ new Set();
|
|
10
|
+
const requiredSuffix = options.entrySuffix?.replace(/^\.?/, ".") ?? "";
|
|
11
|
+
for (const glob of options.globs ?? ["**/*"]) {
|
|
12
|
+
let ext = path.extname(glob);
|
|
13
|
+
if (ext) {
|
|
14
|
+
functionSuffixes.add(requiredSuffix + ext);
|
|
15
|
+
functionGlobs.push(
|
|
16
|
+
requiredSuffix ? glob.replace(ext, requiredSuffix + ext) : glob
|
|
17
|
+
);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
for (ext of options.extensions ?? [".ts", ".js"]) {
|
|
21
|
+
ext = requiredSuffix + ext;
|
|
22
|
+
functionSuffixes.add(ext);
|
|
23
|
+
functionGlobs.push(glob + ext);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const suffixPattern = new RegExp(
|
|
27
|
+
`(${Array.from(functionSuffixes, (e) => e.replace(/\./g, "\\.")).sort((a, b) => b.length - a.length).join("|")})$`
|
|
28
|
+
);
|
|
29
|
+
return {
|
|
30
|
+
globs: functionGlobs,
|
|
31
|
+
suffixPattern
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/config/index.ts
|
|
36
|
+
import * as z2 from "@zod/mini";
|
|
37
|
+
import Joycon from "joycon";
|
|
38
|
+
import path2 from "node:path";
|
|
39
|
+
|
|
40
|
+
// src/config/schema.ts
|
|
41
|
+
import * as z from "@zod/mini";
|
|
42
|
+
var configSchema = z.partial(
|
|
43
|
+
z.interface({
|
|
44
|
+
root: z.string().register(z.globalRegistry, {
|
|
45
|
+
description: dedent`
|
|
46
|
+
The base directory when searching for entry points.
|
|
47
|
+
|
|
48
|
+
Defaults to the config directory (or if no config file is found, the current working directory).
|
|
49
|
+
`
|
|
50
|
+
}),
|
|
51
|
+
globs: z.array(z.string()).register(z.globalRegistry, {
|
|
52
|
+
description: dedent`
|
|
53
|
+
The globs determine which directories are searched for entry points. Only ".ts" and ".js" files are matched, so you're not required to include them in the globs.
|
|
54
|
+
|
|
55
|
+
By default all directories in the "root" are searched.
|
|
56
|
+
`
|
|
57
|
+
}),
|
|
58
|
+
extensions: z.array(z.string()).register(z.globalRegistry, {
|
|
59
|
+
description: dedent`
|
|
60
|
+
The extensions to match for entry points.
|
|
61
|
+
|
|
62
|
+
@default [".ts", ".js"]
|
|
63
|
+
`
|
|
64
|
+
}),
|
|
65
|
+
entrySuffix: z.string().register(z.globalRegistry, {
|
|
66
|
+
description: dedent`
|
|
67
|
+
The entry suffix should be a string like ".task" or ".function" which must be present in the file name (before the extension) or else that file will be ignored when scanning for entry points.
|
|
68
|
+
|
|
69
|
+
It can also be an empty string.
|
|
70
|
+
`
|
|
71
|
+
}),
|
|
72
|
+
adapter: z.enum(["hattip", "node"]).register(z.globalRegistry, {
|
|
73
|
+
description: dedent`
|
|
74
|
+
The adapter wraps your Cloud Run functions at runtime, allowing you to write them with a platform-agnostic HTTP framework, like Hattip.
|
|
75
|
+
|
|
76
|
+
Set this to "node" to skip using an adapter, in which case, your functions should conform to what @google-cloud/functions-framework expects.
|
|
77
|
+
|
|
78
|
+
@default "node"
|
|
79
|
+
`
|
|
80
|
+
}),
|
|
81
|
+
maxInstanceConcurrency: z.union([z.number(), z.record(z.string(), z.number())]).register(z.globalRegistry, {
|
|
82
|
+
description: dedent`
|
|
83
|
+
The maximum number of instances (per function) that can be run concurrently. You can either set the same limit for all functions or set a different limit for each function.
|
|
84
|
+
|
|
85
|
+
@default 5
|
|
86
|
+
`
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// src/config/index.ts
|
|
92
|
+
var joycon = new Joycon();
|
|
93
|
+
function loadConfig(cwd) {
|
|
94
|
+
const result = joycon.loadSync(["crf.config.json"], cwd);
|
|
95
|
+
if (!result.path) {
|
|
96
|
+
return {
|
|
97
|
+
configDir: null
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
...z2.parse(configSchema, result.data),
|
|
102
|
+
configDir: path2.dirname(result.path)
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export {
|
|
107
|
+
getFunctionFilter,
|
|
108
|
+
loadConfig
|
|
109
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getFunctionFilter,
|
|
3
|
+
loadConfig
|
|
4
|
+
} from "./chunk-MOB3GBJW.js";
|
|
5
|
+
|
|
6
|
+
// src/tools/build.ts
|
|
7
|
+
import esbuild from "esbuild";
|
|
8
|
+
import { findUpSync } from "find-up-simple";
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { glob } from "tinyglobby";
|
|
12
|
+
var virtualModuleId = "virtual:cloud-run-functions";
|
|
13
|
+
var virtualModuleNamespace = "crf-functions";
|
|
14
|
+
async function build(root, { define, outdir } = {}) {
|
|
15
|
+
const workingDir = process.cwd();
|
|
16
|
+
const searchDir = path.resolve(workingDir, root ?? "");
|
|
17
|
+
const config = loadConfig(searchDir);
|
|
18
|
+
const functionsRoot = config.configDir ? path.resolve(config.configDir, config.root ?? "") : searchDir;
|
|
19
|
+
const functionFilter = getFunctionFilter(config);
|
|
20
|
+
const adapter = config.adapter ?? "node";
|
|
21
|
+
const entries = await glob(functionFilter.globs, {
|
|
22
|
+
cwd: functionsRoot,
|
|
23
|
+
onlyFiles: true
|
|
24
|
+
});
|
|
25
|
+
const entryPoints = Array.from(
|
|
26
|
+
new Set(entries.map((entry) => entry.split(path.sep).join("/")))
|
|
27
|
+
);
|
|
28
|
+
const routes = entryPoints.map((entry) => {
|
|
29
|
+
const name = entry.replace(functionFilter.suffixPattern, "");
|
|
30
|
+
return {
|
|
31
|
+
entry,
|
|
32
|
+
name,
|
|
33
|
+
path: "/" + name
|
|
34
|
+
};
|
|
35
|
+
}).sort((a, b) => a.path.localeCompare(b.path));
|
|
36
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
37
|
+
for (const route of routes) {
|
|
38
|
+
if (seenPaths.has(route.path)) {
|
|
39
|
+
throw new Error(`Duplicate route detected: ${route.path}`);
|
|
40
|
+
}
|
|
41
|
+
seenPaths.add(route.path);
|
|
42
|
+
}
|
|
43
|
+
const outputDir = path.resolve(workingDir, outdir ?? "dist");
|
|
44
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
45
|
+
const packageDir = findUpSync("dist", {
|
|
46
|
+
cwd: import.meta.dirname,
|
|
47
|
+
type: "directory"
|
|
48
|
+
});
|
|
49
|
+
if (!packageDir) {
|
|
50
|
+
throw new Error("Unable to locate the package dist directory.");
|
|
51
|
+
}
|
|
52
|
+
const source = path.join(packageDir, "targets/build.js");
|
|
53
|
+
const outfile = path.join(outputDir, "index.js");
|
|
54
|
+
const result = await esbuild.build({
|
|
55
|
+
entryPoints: [source],
|
|
56
|
+
absWorkingDir: functionsRoot,
|
|
57
|
+
outfile,
|
|
58
|
+
define,
|
|
59
|
+
bundle: true,
|
|
60
|
+
format: "cjs",
|
|
61
|
+
platform: "node",
|
|
62
|
+
packages: "bundle",
|
|
63
|
+
sourcemap: true,
|
|
64
|
+
sourcesContent: false,
|
|
65
|
+
logOverride: {
|
|
66
|
+
"empty-glob": "silent"
|
|
67
|
+
},
|
|
68
|
+
plugins: [
|
|
69
|
+
{
|
|
70
|
+
name: "crf-virtual-functions",
|
|
71
|
+
setup(build2) {
|
|
72
|
+
const filter = new RegExp(
|
|
73
|
+
`^${virtualModuleId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`
|
|
74
|
+
);
|
|
75
|
+
build2.onResolve({ filter }, () => ({
|
|
76
|
+
path: virtualModuleId,
|
|
77
|
+
namespace: virtualModuleNamespace
|
|
78
|
+
}));
|
|
79
|
+
build2.onLoad({ filter: /.*/, namespace: virtualModuleNamespace }, () => ({
|
|
80
|
+
contents: createVirtualModule({
|
|
81
|
+
routes,
|
|
82
|
+
adapter
|
|
83
|
+
}),
|
|
84
|
+
loader: "js",
|
|
85
|
+
resolveDir: functionsRoot
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
external: adapter === "hattip" ? [] : ["@hattip/adapter-node"]
|
|
91
|
+
});
|
|
92
|
+
console.log(
|
|
93
|
+
`[esbuild] Bundled ${routes.length} function${routes.length === 1 ? "" : "s"} into ${outfile}`
|
|
94
|
+
);
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
function createVirtualModule({
|
|
98
|
+
routes,
|
|
99
|
+
adapter
|
|
100
|
+
}) {
|
|
101
|
+
const imports = routes.map((route, index) => {
|
|
102
|
+
const importPath = route.entry.startsWith(".") ? route.entry : `./${route.entry}`;
|
|
103
|
+
return `import * as route${index} from ${JSON.stringify(importPath)};`;
|
|
104
|
+
});
|
|
105
|
+
const routeEntries = routes.map(
|
|
106
|
+
(route, index) => ` { name: ${JSON.stringify(route.name)}, path: ${JSON.stringify(
|
|
107
|
+
route.path
|
|
108
|
+
)}, module: route${index} },`
|
|
109
|
+
);
|
|
110
|
+
return [
|
|
111
|
+
...imports,
|
|
112
|
+
`export const adapter = ${JSON.stringify(adapter)};`,
|
|
113
|
+
"export const routes = [",
|
|
114
|
+
...routeEntries,
|
|
115
|
+
"];",
|
|
116
|
+
""
|
|
117
|
+
].join("\n");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export {
|
|
121
|
+
build
|
|
122
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/tools/preview.ts
|
|
2
|
+
import { findUpSync } from "find-up-simple";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import $ from "picospawn";
|
|
5
|
+
function preview({ outDir, ...options } = {}) {
|
|
6
|
+
const packageDir = findUpSync("dist", {
|
|
7
|
+
cwd: import.meta.dirname,
|
|
8
|
+
type: "directory"
|
|
9
|
+
});
|
|
10
|
+
const binDir = path.resolve(packageDir, "../node_modules/.bin");
|
|
11
|
+
const sourceDir = path.resolve(process.cwd(), outDir ?? "dist");
|
|
12
|
+
const source = path.join(sourceDir, "index.js");
|
|
13
|
+
return $(
|
|
14
|
+
"functions-framework --target=build --source %s",
|
|
15
|
+
[source],
|
|
16
|
+
{
|
|
17
|
+
stdio: "inherit",
|
|
18
|
+
...options,
|
|
19
|
+
env: {
|
|
20
|
+
...options.env ?? process.env,
|
|
21
|
+
PATH: `${binDir}:${process.env.PATH}`
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
preview
|
|
29
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// node_modules/.pnpm/radashi@12.4.0/node_modules/radashi/dist/radashi.js
|
|
2
|
+
function objectify(array, getKey, getValue = (item) => item) {
|
|
3
|
+
return array.reduce(
|
|
4
|
+
(acc, item) => {
|
|
5
|
+
acc[getKey(item)] = getValue(item);
|
|
6
|
+
return acc;
|
|
7
|
+
},
|
|
8
|
+
{}
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
var TimeoutError = class extends Error {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message ?? "Operation timed out");
|
|
14
|
+
this.name = "TimeoutError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
function timeout(ms, error) {
|
|
18
|
+
return new Promise(
|
|
19
|
+
(_, reject) => setTimeout(
|
|
20
|
+
() => reject(isFunction(error) ? error() : new TimeoutError(error)),
|
|
21
|
+
ms
|
|
22
|
+
)
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
async function toResult(promise) {
|
|
26
|
+
try {
|
|
27
|
+
const result = await promise;
|
|
28
|
+
return [void 0, result];
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (isError(error)) {
|
|
31
|
+
return [error, void 0];
|
|
32
|
+
}
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function dedent(text, ...values) {
|
|
37
|
+
var _a;
|
|
38
|
+
if (isArray(text)) {
|
|
39
|
+
if (values.length > 0) {
|
|
40
|
+
return dedent(
|
|
41
|
+
text.reduce((acc, input, i) => {
|
|
42
|
+
var _a2;
|
|
43
|
+
let value = String(values[i] ?? "");
|
|
44
|
+
const indent2 = value.includes("\n") && ((_a2 = input.match(/[ \t]*(?=[^\n]*$)/)) == null ? void 0 : _a2[0]);
|
|
45
|
+
if (indent2) {
|
|
46
|
+
value = value.replace(/\n(?=[^\n]*?\S)/g, "\n" + indent2);
|
|
47
|
+
}
|
|
48
|
+
return acc + input + value;
|
|
49
|
+
}, "")
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
text = text[0];
|
|
53
|
+
}
|
|
54
|
+
const indent = values[0] ?? ((_a = text.match(/^[ \t]*(?=\S)/m)) == null ? void 0 : _a[0]);
|
|
55
|
+
const output = indent ? text.replace(new RegExp(`^${indent}`, "gm"), "") : text;
|
|
56
|
+
return output.replace(/^[ \t]*\n|\n[ \t]*$/g, "");
|
|
57
|
+
}
|
|
58
|
+
var isArray = /* @__PURE__ */ (() => Array.isArray)();
|
|
59
|
+
var asyncIteratorSymbol = (
|
|
60
|
+
/* c8 ignore next */
|
|
61
|
+
Symbol.asyncIterator || Symbol.for("Symbol.asyncIterator")
|
|
62
|
+
);
|
|
63
|
+
function isError(value) {
|
|
64
|
+
return isTagged(value, "[object Error]");
|
|
65
|
+
}
|
|
66
|
+
function isFunction(value) {
|
|
67
|
+
return typeof value === "function";
|
|
68
|
+
}
|
|
69
|
+
function isNumber(value) {
|
|
70
|
+
return typeof value === "number" && !Number.isNaN(value);
|
|
71
|
+
}
|
|
72
|
+
function isTagged(value, tag) {
|
|
73
|
+
return Object.prototype.toString.call(value) === tag;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export {
|
|
77
|
+
objectify,
|
|
78
|
+
timeout,
|
|
79
|
+
toResult,
|
|
80
|
+
dedent,
|
|
81
|
+
isNumber
|
|
82
|
+
};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
build
|
|
3
|
+
} from "./chunk-NINUPPZA.js";
|
|
4
|
+
import "./chunk-MOB3GBJW.js";
|
|
5
|
+
import "./chunk-SPF3AMMV.js";
|
|
1
6
|
import {
|
|
2
7
|
dev
|
|
3
8
|
} from "./chunk-DG37B63B.js";
|
|
9
|
+
import {
|
|
10
|
+
preview
|
|
11
|
+
} from "./chunk-PFVLROGP.js";
|
|
4
12
|
export {
|
|
5
|
-
|
|
13
|
+
build,
|
|
14
|
+
dev,
|
|
15
|
+
preview
|
|
6
16
|
};
|
package/dist/main.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { }
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/main.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
objectify
|
|
4
|
+
} from "./chunk-SPF3AMMV.js";
|
|
5
|
+
|
|
1
6
|
// src/main.ts
|
|
2
7
|
import {
|
|
8
|
+
array,
|
|
3
9
|
command,
|
|
10
|
+
multioption,
|
|
4
11
|
option,
|
|
5
12
|
optional,
|
|
6
13
|
positional,
|
|
@@ -8,6 +15,20 @@ import {
|
|
|
8
15
|
string,
|
|
9
16
|
subcommands
|
|
10
17
|
} from "cmd-ts";
|
|
18
|
+
|
|
19
|
+
// src/common/parseDefines.ts
|
|
20
|
+
function parseDefines(define) {
|
|
21
|
+
if (!define) {
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
return objectify(
|
|
25
|
+
define.map((d) => d.split(":")),
|
|
26
|
+
([key]) => key,
|
|
27
|
+
([, value]) => value ? isNaN(parseFloat(value)) ? JSON.stringify(value) : value : ""
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/main.ts
|
|
11
32
|
var dev = command({
|
|
12
33
|
name: "dev",
|
|
13
34
|
description: "Start the development server",
|
|
@@ -22,32 +43,74 @@ var dev = command({
|
|
|
22
43
|
long: "port",
|
|
23
44
|
short: "p",
|
|
24
45
|
description: "The port to use for the development server"
|
|
46
|
+
}),
|
|
47
|
+
define: multioption({
|
|
48
|
+
type: optional(array(string)),
|
|
49
|
+
long: "define",
|
|
50
|
+
short: "d",
|
|
51
|
+
description: "Statically replace specific variables in the source code"
|
|
25
52
|
})
|
|
26
53
|
},
|
|
27
|
-
async handler({ root, port }) {
|
|
54
|
+
async handler({ root, port, define }) {
|
|
28
55
|
const { dev: dev2 } = await import("./tools/dev.js");
|
|
29
|
-
await dev2(root, {
|
|
56
|
+
await dev2(root, {
|
|
57
|
+
port,
|
|
58
|
+
define: parseDefines(define)
|
|
59
|
+
});
|
|
30
60
|
}
|
|
31
61
|
});
|
|
32
62
|
var build = command({
|
|
33
63
|
name: "build",
|
|
34
|
-
description: "
|
|
64
|
+
description: "Bundle your functions for deployment",
|
|
35
65
|
args: {
|
|
36
66
|
root: positional({
|
|
37
67
|
type: optional(string),
|
|
38
68
|
displayName: "root",
|
|
39
69
|
description: "Directory to search for function entrypoints"
|
|
70
|
+
}),
|
|
71
|
+
outdir: option({
|
|
72
|
+
type: optional(string),
|
|
73
|
+
long: "outdir",
|
|
74
|
+
short: "o",
|
|
75
|
+
description: "The directory to write bundled output to"
|
|
76
|
+
}),
|
|
77
|
+
define: multioption({
|
|
78
|
+
type: optional(array(string)),
|
|
79
|
+
long: "define",
|
|
80
|
+
short: "d",
|
|
81
|
+
description: "Statically replace specific variables in the source code"
|
|
82
|
+
})
|
|
83
|
+
},
|
|
84
|
+
async handler({ root, outdir, define }) {
|
|
85
|
+
const { build: build2 } = await import("./tools/build.js");
|
|
86
|
+
await build2(root, {
|
|
87
|
+
outdir,
|
|
88
|
+
define: parseDefines(define)
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
var preview = command({
|
|
93
|
+
name: "preview",
|
|
94
|
+
description: "Preview bundled functions locally",
|
|
95
|
+
args: {
|
|
96
|
+
outDir: option({
|
|
97
|
+
type: optional(string),
|
|
98
|
+
long: "outDir",
|
|
99
|
+
short: "o",
|
|
100
|
+
description: "The directory containing the bundled output"
|
|
40
101
|
})
|
|
41
102
|
},
|
|
42
|
-
async handler() {
|
|
43
|
-
|
|
103
|
+
async handler({ outDir }) {
|
|
104
|
+
const { preview: preview2 } = await import("./tools/preview.js");
|
|
105
|
+
await preview2({ outDir });
|
|
44
106
|
}
|
|
45
107
|
});
|
|
46
108
|
var cli = subcommands({
|
|
47
109
|
name: "cloud-run-functions",
|
|
48
110
|
cmds: {
|
|
49
111
|
dev,
|
|
50
|
-
build
|
|
112
|
+
build,
|
|
113
|
+
preview
|
|
51
114
|
}
|
|
52
115
|
});
|
|
53
116
|
await run(cli, process.argv.slice(2));
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// src/targets/build.ts
|
|
2
|
+
import "source-map-support/register.js";
|
|
3
|
+
import functions from "@google-cloud/functions-framework";
|
|
4
|
+
import { adapter, routes } from "virtual:cloud-run-functions";
|
|
5
|
+
var routesByPath = new Map(routes.map((route) => [route.path, route]));
|
|
6
|
+
var handlerCache = /* @__PURE__ */ new Map();
|
|
7
|
+
function unwrapHandler(module) {
|
|
8
|
+
let handler = module;
|
|
9
|
+
while (handler && typeof handler !== "function") {
|
|
10
|
+
handler = handler.default;
|
|
11
|
+
}
|
|
12
|
+
return typeof handler === "function" ? handler : null;
|
|
13
|
+
}
|
|
14
|
+
async function getHandler(route) {
|
|
15
|
+
const cached = handlerCache.get(route.path);
|
|
16
|
+
if (cached) {
|
|
17
|
+
return cached;
|
|
18
|
+
}
|
|
19
|
+
let handler = unwrapHandler(route.module);
|
|
20
|
+
if (!handler) {
|
|
21
|
+
throw new Error(`Task ${route.name} is not a function.`);
|
|
22
|
+
}
|
|
23
|
+
switch (adapter) {
|
|
24
|
+
case "hattip": {
|
|
25
|
+
const { createMiddleware } = await import("@hattip/adapter-node");
|
|
26
|
+
handler = createMiddleware(handler);
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
handlerCache.set(route.path, handler);
|
|
31
|
+
return handler;
|
|
32
|
+
}
|
|
33
|
+
async function match(url) {
|
|
34
|
+
const route = routesByPath.get(url.pathname);
|
|
35
|
+
if (!route) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return getHandler(route);
|
|
39
|
+
}
|
|
40
|
+
functions.http("build", async (req, res) => {
|
|
41
|
+
const url = new URL(req.url, "http://" + req.headers.host);
|
|
42
|
+
const handler = await match(url);
|
|
43
|
+
if (handler) {
|
|
44
|
+
try {
|
|
45
|
+
await handler(req, res);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(error);
|
|
48
|
+
res.status(500).end();
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
res.status(404).end();
|
|
52
|
+
}
|
|
53
|
+
});
|
package/dist/targets/dev.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getFunctionFilter,
|
|
3
|
+
loadConfig
|
|
4
|
+
} from "../chunk-MOB3GBJW.js";
|
|
5
|
+
import {
|
|
6
|
+
isNumber,
|
|
7
|
+
timeout,
|
|
8
|
+
toResult
|
|
9
|
+
} from "../chunk-SPF3AMMV.js";
|
|
10
|
+
|
|
1
11
|
// src/targets/dev.ts
|
|
2
12
|
import "source-map-support/register.js";
|
|
3
13
|
import functions from "@google-cloud/functions-framework";
|
|
@@ -6,73 +16,7 @@ import { findUpSync } from "find-up-simple";
|
|
|
6
16
|
import fs2 from "node:fs";
|
|
7
17
|
import { Module } from "node:module";
|
|
8
18
|
import os from "node:os";
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
// node_modules/.pnpm/radashi@12.4.0/node_modules/radashi/dist/radashi.js
|
|
12
|
-
var TimeoutError = class extends Error {
|
|
13
|
-
constructor(message) {
|
|
14
|
-
super(message ?? "Operation timed out");
|
|
15
|
-
this.name = "TimeoutError";
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
function timeout(ms, error) {
|
|
19
|
-
return new Promise(
|
|
20
|
-
(_, reject) => setTimeout(
|
|
21
|
-
() => reject(isFunction(error) ? error() : new TimeoutError(error)),
|
|
22
|
-
ms
|
|
23
|
-
)
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
async function toResult(promise) {
|
|
27
|
-
try {
|
|
28
|
-
const result = await promise;
|
|
29
|
-
return [void 0, result];
|
|
30
|
-
} catch (error) {
|
|
31
|
-
if (isError(error)) {
|
|
32
|
-
return [error, void 0];
|
|
33
|
-
}
|
|
34
|
-
throw error;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function dedent(text, ...values) {
|
|
38
|
-
var _a;
|
|
39
|
-
if (isArray(text)) {
|
|
40
|
-
if (values.length > 0) {
|
|
41
|
-
return dedent(
|
|
42
|
-
text.reduce((acc, input, i) => {
|
|
43
|
-
var _a2;
|
|
44
|
-
let value = String(values[i] ?? "");
|
|
45
|
-
const indent2 = value.includes("\n") && ((_a2 = input.match(/[ \t]*(?=[^\n]*$)/)) == null ? void 0 : _a2[0]);
|
|
46
|
-
if (indent2) {
|
|
47
|
-
value = value.replace(/\n(?=[^\n]*?\S)/g, "\n" + indent2);
|
|
48
|
-
}
|
|
49
|
-
return acc + input + value;
|
|
50
|
-
}, "")
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
text = text[0];
|
|
54
|
-
}
|
|
55
|
-
const indent = values[0] ?? ((_a = text.match(/^[ \t]*(?=\S)/m)) == null ? void 0 : _a[0]);
|
|
56
|
-
const output = indent ? text.replace(new RegExp(`^${indent}`, "gm"), "") : text;
|
|
57
|
-
return output.replace(/^[ \t]*\n|\n[ \t]*$/g, "");
|
|
58
|
-
}
|
|
59
|
-
var isArray = /* @__PURE__ */ (() => Array.isArray)();
|
|
60
|
-
var asyncIteratorSymbol = (
|
|
61
|
-
/* c8 ignore next */
|
|
62
|
-
Symbol.asyncIterator || Symbol.for("Symbol.asyncIterator")
|
|
63
|
-
);
|
|
64
|
-
function isError(value) {
|
|
65
|
-
return isTagged(value, "[object Error]");
|
|
66
|
-
}
|
|
67
|
-
function isFunction(value) {
|
|
68
|
-
return typeof value === "function";
|
|
69
|
-
}
|
|
70
|
-
function isNumber(value) {
|
|
71
|
-
return typeof value === "number" && !Number.isNaN(value);
|
|
72
|
-
}
|
|
73
|
-
function isTagged(value, tag) {
|
|
74
|
-
return Object.prototype.toString.call(value) === tag;
|
|
75
|
-
}
|
|
19
|
+
import path from "node:path";
|
|
76
20
|
|
|
77
21
|
// src/common/emptyDir.ts
|
|
78
22
|
import fs from "node:fs";
|
|
@@ -82,122 +26,21 @@ function emptyDir(dir) {
|
|
|
82
26
|
return dir;
|
|
83
27
|
}
|
|
84
28
|
|
|
85
|
-
// src/common/functionFilter.ts
|
|
86
|
-
import path from "node:path";
|
|
87
|
-
function getFunctionFilter(options) {
|
|
88
|
-
const functionGlobs = [];
|
|
89
|
-
const functionSuffixes = /* @__PURE__ */ new Set();
|
|
90
|
-
const requiredSuffix = options.entrySuffix?.replace(/^\.?/, ".") ?? "";
|
|
91
|
-
for (const glob of options.globs ?? ["**/*"]) {
|
|
92
|
-
let ext = path.extname(glob);
|
|
93
|
-
if (ext) {
|
|
94
|
-
functionSuffixes.add(requiredSuffix + ext);
|
|
95
|
-
functionGlobs.push(
|
|
96
|
-
requiredSuffix ? glob.replace(ext, requiredSuffix + ext) : glob
|
|
97
|
-
);
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
for (ext of options.extensions ?? [".ts", ".js"]) {
|
|
101
|
-
ext = requiredSuffix + ext;
|
|
102
|
-
functionSuffixes.add(ext);
|
|
103
|
-
functionGlobs.push(glob + ext);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
const suffixPattern = new RegExp(
|
|
107
|
-
`(${Array.from(functionSuffixes, (e) => e.replace(/\./g, "\\.")).sort((a, b) => b.length - a.length).join("|")})$`
|
|
108
|
-
);
|
|
109
|
-
return {
|
|
110
|
-
globs: functionGlobs,
|
|
111
|
-
suffixPattern
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
29
|
// src/common/hash.ts
|
|
116
30
|
import crypto from "node:crypto";
|
|
117
31
|
function hash(data, len) {
|
|
118
32
|
return crypto.createHash("sha256").update(data).digest("hex").slice(0, len);
|
|
119
33
|
}
|
|
120
34
|
|
|
121
|
-
// src/config/index.ts
|
|
122
|
-
import * as z2 from "@zod/mini";
|
|
123
|
-
import Joycon from "joycon";
|
|
124
|
-
import path2 from "node:path";
|
|
125
|
-
|
|
126
|
-
// src/config/schema.ts
|
|
127
|
-
import * as z from "@zod/mini";
|
|
128
|
-
var configSchema = z.partial(
|
|
129
|
-
z.interface({
|
|
130
|
-
root: z.string().register(z.globalRegistry, {
|
|
131
|
-
description: dedent`
|
|
132
|
-
The base directory when searching for entry points.
|
|
133
|
-
|
|
134
|
-
Defaults to the config directory (or if no config file is found, the current working directory).
|
|
135
|
-
`
|
|
136
|
-
}),
|
|
137
|
-
globs: z.array(z.string()).register(z.globalRegistry, {
|
|
138
|
-
description: dedent`
|
|
139
|
-
The globs determine which directories are searched for entry points. Only ".ts" and ".js" files are matched, so you're not required to include them in the globs.
|
|
140
|
-
|
|
141
|
-
By default all directories in the "root" are searched.
|
|
142
|
-
`
|
|
143
|
-
}),
|
|
144
|
-
extensions: z.array(z.string()).register(z.globalRegistry, {
|
|
145
|
-
description: dedent`
|
|
146
|
-
The extensions to match for entry points.
|
|
147
|
-
|
|
148
|
-
@default [".ts", ".js"]
|
|
149
|
-
`
|
|
150
|
-
}),
|
|
151
|
-
entrySuffix: z.string().register(z.globalRegistry, {
|
|
152
|
-
description: dedent`
|
|
153
|
-
The entry suffix should be a string like ".task" or ".function" which must be present in the file name (before the extension) or else that file will be ignored when scanning for entry points.
|
|
154
|
-
|
|
155
|
-
It can also be an empty string.
|
|
156
|
-
`
|
|
157
|
-
}),
|
|
158
|
-
adapter: z.enum(["hattip", "node"]).register(z.globalRegistry, {
|
|
159
|
-
description: dedent`
|
|
160
|
-
The adapter wraps your Cloud Run functions at runtime, allowing you to write them with a platform-agnostic HTTP framework, like Hattip.
|
|
161
|
-
|
|
162
|
-
Set this to "node" to skip using an adapter, in which case, your functions should conform to what @google-cloud/functions-framework expects.
|
|
163
|
-
|
|
164
|
-
@default "node"
|
|
165
|
-
`
|
|
166
|
-
}),
|
|
167
|
-
maxInstanceConcurrency: z.union([z.number(), z.record(z.string(), z.number())]).register(z.globalRegistry, {
|
|
168
|
-
description: dedent`
|
|
169
|
-
The maximum number of instances (per function) that can be run concurrently. You can either set the same limit for all functions or set a different limit for each function.
|
|
170
|
-
|
|
171
|
-
@default 5
|
|
172
|
-
`
|
|
173
|
-
})
|
|
174
|
-
})
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
// src/config/index.ts
|
|
178
|
-
var joycon = new Joycon();
|
|
179
|
-
function loadConfig(cwd) {
|
|
180
|
-
const result = joycon.loadSync(["crf.config.json"], cwd);
|
|
181
|
-
if (!result.path) {
|
|
182
|
-
return {
|
|
183
|
-
configDir: null
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
return {
|
|
187
|
-
...z2.parse(configSchema, result.data),
|
|
188
|
-
configDir: path2.dirname(result.path)
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
35
|
// src/targets/dev.ts
|
|
193
36
|
async function createBuild() {
|
|
194
37
|
const options = JSON.parse(process.env.CRF_OPTIONS);
|
|
195
|
-
const searchDir =
|
|
38
|
+
const searchDir = path.resolve(options.workingDir, options.searchDir ?? "");
|
|
196
39
|
const config = loadConfig(searchDir);
|
|
197
|
-
const root = config.configDir ?
|
|
40
|
+
const root = config.configDir ? path.resolve(config.configDir, config.root ?? "") : searchDir;
|
|
198
41
|
const functionFilter = getFunctionFilter(config);
|
|
199
42
|
const cacheDir = emptyDir(
|
|
200
|
-
|
|
43
|
+
path.join(
|
|
201
44
|
fs2.realpathSync(os.tmpdir()),
|
|
202
45
|
"cloud-run-functions-" + hash(root, 8)
|
|
203
46
|
)
|
|
@@ -223,14 +66,26 @@ async function createBuild() {
|
|
|
223
66
|
{
|
|
224
67
|
name: "build-status",
|
|
225
68
|
setup(build) {
|
|
69
|
+
let startTime;
|
|
226
70
|
pendingBuild = Promise.withResolvers();
|
|
227
71
|
build.onStart(() => {
|
|
228
72
|
pendingBuild ??= Promise.withResolvers();
|
|
73
|
+
startTime = Date.now();
|
|
229
74
|
});
|
|
230
75
|
build.onEnd((result) => {
|
|
231
76
|
if (pendingBuild) {
|
|
232
77
|
pendingBuild.resolve(result);
|
|
233
78
|
pendingBuild = void 0;
|
|
79
|
+
console.log(
|
|
80
|
+
`[%s] %s your Cloud Run functions in %sms.`,
|
|
81
|
+
(/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
|
|
82
|
+
hour: "2-digit",
|
|
83
|
+
minute: "2-digit",
|
|
84
|
+
hour12: false
|
|
85
|
+
}),
|
|
86
|
+
finishedBuild ? "Rebuilt" : "Built",
|
|
87
|
+
Date.now() - startTime
|
|
88
|
+
);
|
|
234
89
|
}
|
|
235
90
|
finishedBuild = result;
|
|
236
91
|
});
|
|
@@ -287,7 +142,7 @@ async function createBuild() {
|
|
|
287
142
|
taskState.running++;
|
|
288
143
|
taskStates.set(taskName, taskState);
|
|
289
144
|
const require2 = Module.createRequire(import.meta.filename);
|
|
290
|
-
let taskHandler = require2(
|
|
145
|
+
let taskHandler = require2(path.join(root, file));
|
|
291
146
|
while (taskHandler && typeof taskHandler !== "function") {
|
|
292
147
|
taskHandler = taskHandler.default;
|
|
293
148
|
}
|
package/dist/tools/build.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import esbuild from 'esbuild';
|
|
2
|
+
|
|
1
3
|
type BuildOptions = {
|
|
2
4
|
/**
|
|
3
5
|
* Statically replace specific variables in the source code.
|
|
@@ -13,5 +15,32 @@ type BuildOptions = {
|
|
|
13
15
|
*/
|
|
14
16
|
define?: Record<string, string>;
|
|
15
17
|
};
|
|
18
|
+
type BundleOptions = BuildOptions & {
|
|
19
|
+
/**
|
|
20
|
+
* The directory to write the bundled output to.
|
|
21
|
+
* @default "dist"
|
|
22
|
+
*/
|
|
23
|
+
outdir?: string;
|
|
24
|
+
};
|
|
25
|
+
declare function build(root?: string, { define, outdir }?: BundleOptions): Promise<esbuild.BuildResult<{
|
|
26
|
+
entryPoints: string[];
|
|
27
|
+
absWorkingDir: string;
|
|
28
|
+
outfile: string;
|
|
29
|
+
define: Record<string, string> | undefined;
|
|
30
|
+
bundle: true;
|
|
31
|
+
format: "cjs";
|
|
32
|
+
platform: "node";
|
|
33
|
+
packages: "bundle";
|
|
34
|
+
sourcemap: true;
|
|
35
|
+
sourcesContent: false;
|
|
36
|
+
logOverride: {
|
|
37
|
+
'empty-glob': "silent";
|
|
38
|
+
};
|
|
39
|
+
plugins: {
|
|
40
|
+
name: string;
|
|
41
|
+
setup(build: esbuild.PluginBuild): void;
|
|
42
|
+
}[];
|
|
43
|
+
external: string[];
|
|
44
|
+
}>>;
|
|
16
45
|
|
|
17
|
-
export type
|
|
46
|
+
export { type BuildOptions, type BundleOptions, build };
|
package/dist/tools/build.js
CHANGED
package/dist/tools/dev.d.ts
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as picospawn from 'picospawn';
|
|
2
|
+
import { PicospawnOptions } from 'picospawn';
|
|
3
|
+
|
|
4
|
+
interface PreviewOptions extends PicospawnOptions {
|
|
5
|
+
/**
|
|
6
|
+
* The directory containing the bundled output.
|
|
7
|
+
* @default "dist"
|
|
8
|
+
*/
|
|
9
|
+
outDir?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Start the preview server for the bundled output.
|
|
13
|
+
*/
|
|
14
|
+
declare function preview({ outDir, ...options }?: PreviewOptions): picospawn.PicospawnPromise<unknown>;
|
|
15
|
+
|
|
16
|
+
export { type PreviewOptions, preview };
|