cloud-run-functions 0.1.0 → 0.1.1
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/config.schema.json +24 -0
- package/dist/chunk-DG37B63B.js +33 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/targets/dev.js +126 -38
- package/dist/tools/build.d.ts +16 -1
- package/dist/tools/dev.d.ts +11 -2
- package/dist/tools/dev.js +1 -1
- package/package.json +3 -2
- package/readme.md +4 -0
- package/dist/chunk-XTHOFO7F.js +0 -25
package/config.schema.json
CHANGED
|
@@ -64,6 +64,30 @@
|
|
|
64
64
|
"type": "null"
|
|
65
65
|
}
|
|
66
66
|
]
|
|
67
|
+
},
|
|
68
|
+
"maxInstanceConcurrency": {
|
|
69
|
+
"oneOf": [
|
|
70
|
+
{
|
|
71
|
+
"description": "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.\n\n@default 5",
|
|
72
|
+
"oneOf": [
|
|
73
|
+
{
|
|
74
|
+
"type": "number"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"type": "object",
|
|
78
|
+
"propertyNames": {
|
|
79
|
+
"type": "string"
|
|
80
|
+
},
|
|
81
|
+
"additionalProperties": {
|
|
82
|
+
"type": "number"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"type": "null"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
67
91
|
}
|
|
68
92
|
},
|
|
69
93
|
"required": []
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/tools/dev.ts
|
|
2
|
+
import { findUpSync } from "find-up-simple";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import $ from "picospawn";
|
|
5
|
+
function dev(root, { port, define, ...options } = {}) {
|
|
6
|
+
const packageDir = findUpSync("dist", {
|
|
7
|
+
cwd: import.meta.dirname,
|
|
8
|
+
type: "directory"
|
|
9
|
+
});
|
|
10
|
+
const source = path.join(packageDir, "targets/dev.js");
|
|
11
|
+
const binDir = path.resolve(packageDir, "../node_modules/.bin");
|
|
12
|
+
return $(
|
|
13
|
+
"functions-framework --target=dev --source %s",
|
|
14
|
+
[source, port != null && ["--port", port.toString()]],
|
|
15
|
+
{
|
|
16
|
+
stdio: "inherit",
|
|
17
|
+
...options,
|
|
18
|
+
env: {
|
|
19
|
+
...options.env ?? process.env,
|
|
20
|
+
CRF_OPTIONS: JSON.stringify({
|
|
21
|
+
searchDir: root,
|
|
22
|
+
workingDir: process.cwd(),
|
|
23
|
+
define
|
|
24
|
+
}),
|
|
25
|
+
PATH: `${binDir}:${process.env.PATH}`
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
dev
|
|
33
|
+
};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/targets/dev.js
CHANGED
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
// src/targets/dev.ts
|
|
2
|
+
import "source-map-support/register.js";
|
|
2
3
|
import functions from "@google-cloud/functions-framework";
|
|
3
4
|
import esbuild from "esbuild";
|
|
4
|
-
import
|
|
5
|
+
import fs2 from "node:fs";
|
|
6
|
+
import { Module } from "node:module";
|
|
5
7
|
import os from "node:os";
|
|
6
8
|
import path2 from "node:path";
|
|
7
9
|
|
|
8
|
-
// src/config/index.ts
|
|
9
|
-
import * as z2 from "@zod/mini";
|
|
10
|
-
import Joycon from "joycon";
|
|
11
|
-
import path from "node:path";
|
|
12
|
-
|
|
13
|
-
// src/config/schema.ts
|
|
14
|
-
import * as z from "@zod/mini";
|
|
15
|
-
|
|
16
10
|
// node_modules/.pnpm/radashi@12.4.0/node_modules/radashi/dist/radashi.js
|
|
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
|
+
}
|
|
17
36
|
function dedent(text, ...values) {
|
|
18
37
|
var _a;
|
|
19
38
|
if (isArray(text)) {
|
|
@@ -41,8 +60,26 @@ var asyncIteratorSymbol = (
|
|
|
41
60
|
/* c8 ignore next */
|
|
42
61
|
Symbol.asyncIterator || Symbol.for("Symbol.asyncIterator")
|
|
43
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
|
+
// src/config/index.ts
|
|
77
|
+
import * as z2 from "@zod/mini";
|
|
78
|
+
import Joycon from "joycon";
|
|
79
|
+
import path from "node:path";
|
|
44
80
|
|
|
45
81
|
// src/config/schema.ts
|
|
82
|
+
import * as z from "@zod/mini";
|
|
46
83
|
var configSchema = z.partial(
|
|
47
84
|
z.interface({
|
|
48
85
|
root: z.string().register(z.globalRegistry, {
|
|
@@ -81,6 +118,13 @@ var configSchema = z.partial(
|
|
|
81
118
|
|
|
82
119
|
@default "node"
|
|
83
120
|
`
|
|
121
|
+
}),
|
|
122
|
+
maxInstanceConcurrency: z.union([z.number(), z.record(z.string(), z.number())]).register(z.globalRegistry, {
|
|
123
|
+
description: dedent`
|
|
124
|
+
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.
|
|
125
|
+
|
|
126
|
+
@default 5
|
|
127
|
+
`
|
|
84
128
|
})
|
|
85
129
|
})
|
|
86
130
|
);
|
|
@@ -116,11 +160,10 @@ function hash(data, len) {
|
|
|
116
160
|
|
|
117
161
|
// src/targets/dev.ts
|
|
118
162
|
async function createBuild() {
|
|
119
|
-
const
|
|
120
|
-
const searchDir =
|
|
163
|
+
const options = JSON.parse(process.env.CRF_OPTIONS);
|
|
164
|
+
const searchDir = path2.resolve(options.workingDir, options.searchDir ?? "");
|
|
121
165
|
const config = loadConfig(searchDir);
|
|
122
166
|
const root = config.configDir ? path2.resolve(config.configDir, config.root ?? "") : searchDir;
|
|
123
|
-
let pendingBuild;
|
|
124
167
|
const entryPoints = [];
|
|
125
168
|
const requiredSuffix = config.entrySuffix?.replace(/^\.?/, ".") ?? "";
|
|
126
169
|
const knownSuffixes = /* @__PURE__ */ new Set();
|
|
@@ -142,27 +185,25 @@ async function createBuild() {
|
|
|
142
185
|
const knownSuffixesRE = new RegExp(
|
|
143
186
|
`(${Array.from(knownSuffixes, (e) => e.replace(/\./g, "\\.")).sort((a, b) => b.length - a.length).join("|")})$`
|
|
144
187
|
);
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
nodeModulesDir ? path2.join(nodeModulesDir, `.cache/cloud-run-functions-${hash(root, 8)}`) : path2.join(os.tmpdir(), `cloud-run-functions-${hash(root, 8)}`)
|
|
188
|
+
const cacheDir = emptyDir(
|
|
189
|
+
path2.join(
|
|
190
|
+
fs2.realpathSync(os.tmpdir()),
|
|
191
|
+
"cloud-run-functions-" + hash(root, 8)
|
|
192
|
+
)
|
|
151
193
|
);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
outDir,
|
|
155
|
-
entryPoints,
|
|
156
|
-
knownSuffixesRE
|
|
157
|
-
});
|
|
194
|
+
let pendingBuild;
|
|
195
|
+
let finishedBuild;
|
|
158
196
|
const context = await esbuild.context({
|
|
159
197
|
entryPoints,
|
|
160
198
|
absWorkingDir: root,
|
|
161
|
-
outdir:
|
|
199
|
+
outdir: cacheDir,
|
|
200
|
+
define: options.define,
|
|
162
201
|
bundle: true,
|
|
163
|
-
format: "
|
|
164
|
-
|
|
202
|
+
format: "cjs",
|
|
203
|
+
platform: "node",
|
|
204
|
+
packages: "bundle",
|
|
165
205
|
sourcemap: true,
|
|
206
|
+
sourcesContent: false,
|
|
166
207
|
metafile: true,
|
|
167
208
|
logOverride: {
|
|
168
209
|
"empty-glob": "silent"
|
|
@@ -171,11 +212,16 @@ async function createBuild() {
|
|
|
171
212
|
{
|
|
172
213
|
name: "build-status",
|
|
173
214
|
setup(build) {
|
|
215
|
+
pendingBuild = Promise.withResolvers();
|
|
174
216
|
build.onStart(() => {
|
|
175
|
-
pendingBuild
|
|
217
|
+
pendingBuild ??= Promise.withResolvers();
|
|
176
218
|
});
|
|
177
219
|
build.onEnd((result) => {
|
|
178
|
-
pendingBuild
|
|
220
|
+
if (pendingBuild) {
|
|
221
|
+
pendingBuild.resolve(result);
|
|
222
|
+
pendingBuild = void 0;
|
|
223
|
+
}
|
|
224
|
+
finishedBuild = result;
|
|
179
225
|
});
|
|
180
226
|
}
|
|
181
227
|
}
|
|
@@ -186,11 +232,16 @@ async function createBuild() {
|
|
|
186
232
|
try {
|
|
187
233
|
const dotenv = await import("dotenv");
|
|
188
234
|
dotenv.config();
|
|
235
|
+
console.log("[dotenv] Environment variables loaded.");
|
|
189
236
|
} catch {
|
|
190
237
|
}
|
|
238
|
+
const taskStates = /* @__PURE__ */ new Map();
|
|
191
239
|
return {
|
|
192
240
|
async match(url) {
|
|
193
|
-
const result = await pendingBuild
|
|
241
|
+
const result = await pendingBuild?.promise ?? finishedBuild;
|
|
242
|
+
if (!result) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
194
245
|
for (const [file, output] of Object.entries(
|
|
195
246
|
result.metafile?.outputs ?? {}
|
|
196
247
|
)) {
|
|
@@ -199,18 +250,50 @@ async function createBuild() {
|
|
|
199
250
|
}
|
|
200
251
|
const taskName = output.entryPoint.replace(knownSuffixesRE, "");
|
|
201
252
|
if (url.pathname === "/" + taskName) {
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
253
|
+
const taskState = taskStates.get(taskName) ?? {
|
|
254
|
+
running: 0,
|
|
255
|
+
queue: []
|
|
256
|
+
};
|
|
257
|
+
const taskConcurrency = isNumber(config.maxInstanceConcurrency) ? config.maxInstanceConcurrency : config.maxInstanceConcurrency?.[taskName] ?? 5;
|
|
258
|
+
if (taskState.running >= taskConcurrency) {
|
|
259
|
+
const ticket = Promise.withResolvers();
|
|
260
|
+
taskState.queue.push(ticket);
|
|
261
|
+
const [error] = await toResult(
|
|
262
|
+
Promise.race([ticket.promise, timeout(3e4)])
|
|
263
|
+
);
|
|
264
|
+
if (error) {
|
|
265
|
+
return (_req, res) => {
|
|
266
|
+
res.status(429).end();
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
taskState.running++;
|
|
271
|
+
taskStates.set(taskName, taskState);
|
|
272
|
+
const require2 = Module.createRequire(import.meta.filename);
|
|
273
|
+
let taskHandler = require2(path2.join(root, file));
|
|
274
|
+
while (taskHandler && typeof taskHandler !== "function") {
|
|
275
|
+
taskHandler = taskHandler.default;
|
|
276
|
+
}
|
|
277
|
+
if (!taskHandler) {
|
|
278
|
+
return () => {
|
|
279
|
+
throw new Error(`Task ${taskName} is not a function.`);
|
|
280
|
+
};
|
|
281
|
+
}
|
|
206
282
|
switch (config.adapter) {
|
|
207
283
|
case "hattip": {
|
|
208
284
|
const { createMiddleware } = await import("@hattip/adapter-node");
|
|
209
|
-
|
|
285
|
+
taskHandler = createMiddleware(taskHandler);
|
|
210
286
|
}
|
|
211
|
-
default:
|
|
212
|
-
return taskHandler;
|
|
213
287
|
}
|
|
288
|
+
return (req, res) => {
|
|
289
|
+
const end = res.end.bind(res);
|
|
290
|
+
res.end = (...args) => {
|
|
291
|
+
taskState.running--;
|
|
292
|
+
taskState.queue.shift()?.resolve();
|
|
293
|
+
return end(...args);
|
|
294
|
+
};
|
|
295
|
+
return taskHandler(req, res);
|
|
296
|
+
};
|
|
214
297
|
}
|
|
215
298
|
}
|
|
216
299
|
return null;
|
|
@@ -223,7 +306,12 @@ functions.http("dev", async (req, res) => {
|
|
|
223
306
|
const build = await buildPromise;
|
|
224
307
|
const handler = await build.match(url);
|
|
225
308
|
if (handler) {
|
|
226
|
-
|
|
309
|
+
try {
|
|
310
|
+
await handler(req, res);
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.error(error);
|
|
313
|
+
res.status(500).end();
|
|
314
|
+
}
|
|
227
315
|
} else {
|
|
228
316
|
res.status(404).end();
|
|
229
317
|
}
|
package/dist/tools/build.d.ts
CHANGED
|
@@ -1,2 +1,17 @@
|
|
|
1
|
+
type BuildOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* Statically replace specific variables in the source code.
|
|
4
|
+
*
|
|
5
|
+
* ⚠️ The value must be valid JavaScript syntax!
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* define: {
|
|
10
|
+
* 'process.env.NODE_ENV': '"development"',
|
|
11
|
+
* }
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
define?: Record<string, string>;
|
|
15
|
+
};
|
|
1
16
|
|
|
2
|
-
export {
|
|
17
|
+
export type { BuildOptions };
|
package/dist/tools/dev.d.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import * as picospawn from 'picospawn';
|
|
2
|
+
import { PicospawnOptions } from 'picospawn';
|
|
3
|
+
import { BuildOptions } from './build.js';
|
|
2
4
|
|
|
5
|
+
interface DevOptions extends PicospawnOptions, BuildOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Customize the port to use for the development server.
|
|
8
|
+
* @default 8080
|
|
9
|
+
*/
|
|
10
|
+
port?: string | number;
|
|
11
|
+
}
|
|
3
12
|
/**
|
|
4
13
|
* Start the development server in a separate process.
|
|
5
14
|
*/
|
|
6
|
-
declare function dev(root?: string): picospawn.PicospawnPromise<
|
|
15
|
+
declare function dev(root?: string, { port, define, ...options }?: DevOptions): picospawn.PicospawnPromise<unknown>;
|
|
7
16
|
|
|
8
|
-
export { dev };
|
|
17
|
+
export { type DevOptions, dev };
|
package/dist/tools/dev.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloud-run-functions",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.1",
|
|
5
5
|
"bin": "./dist/main.js",
|
|
6
6
|
"exports": {
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"find-up-simple": "^1.0.1",
|
|
40
40
|
"joycon": "^3.1.1",
|
|
41
41
|
"ordana": "^0.4.0",
|
|
42
|
-
"picospawn": "^0.3.
|
|
42
|
+
"picospawn": "^0.3.2",
|
|
43
|
+
"source-map-support": "^0.5.21",
|
|
43
44
|
"tinyglobby": "^0.2.13"
|
|
44
45
|
},
|
|
45
46
|
"peerDependencies": {
|
package/readme.md
CHANGED
|
@@ -27,3 +27,7 @@ When you're ready to deploy, use the `build` command to bundle your functions.
|
|
|
27
27
|
```sh
|
|
28
28
|
npx cloud-run-functions build ./path/to/functions/
|
|
29
29
|
```
|
|
30
|
+
|
|
31
|
+
## Tips
|
|
32
|
+
|
|
33
|
+
- If you have the [dotenv](https://www.npmjs.com/package/dotenv) package installed, the dev server will import it and automatically load environment variables from the closest `.env` file. Note that environment variables in `.env` won't override existing `process.env` values.
|
package/dist/chunk-XTHOFO7F.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// src/tools/dev.ts
|
|
2
|
-
import { findUpSync } from "find-up-simple";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import $ from "picospawn";
|
|
5
|
-
function dev(root) {
|
|
6
|
-
const packageDir = findUpSync("dist", {
|
|
7
|
-
cwd: import.meta.dirname,
|
|
8
|
-
type: "directory"
|
|
9
|
-
});
|
|
10
|
-
const source = path.join(packageDir, "targets/dev.js");
|
|
11
|
-
const binDir = path.resolve(packageDir, "../node_modules/.bin");
|
|
12
|
-
return $("functions-framework --target=dev --source", [source], {
|
|
13
|
-
stdio: "inherit",
|
|
14
|
-
env: {
|
|
15
|
-
...process.env,
|
|
16
|
-
CRF_ROOT: root,
|
|
17
|
-
CALLER_DIR: process.cwd(),
|
|
18
|
-
PATH: `${binDir}:${process.env.PATH}`
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export {
|
|
24
|
-
dev
|
|
25
|
-
};
|