serverless-offline 14.5.0 → 14.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serverless-offline",
3
- "version": "14.5.0",
3
+ "version": "14.7.0",
4
4
  "description": "Emulate AWS λ and API Gateway locally when developing your Serverless project",
5
5
  "license": "MIT",
6
6
  "exports": {
@@ -13,9 +13,9 @@
13
13
  "code-quality": "npm run prettier && npm run lint",
14
14
  "lint": "eslint .",
15
15
  "lint:fix": "eslint . --fix",
16
- "list-contributors": "echo 'clone https://github.com/mgechev/github-contributors-list.git first, then run npm install' && cd ../github-contributors-list && node bin/githubcontrib --owner dherault --repo serverless-offline --sortBy contributions --showlogin true --sortOrder desc > contributors.md",
17
- "prepare-release": "commit-and-tag-version && prettier --write CHANGELOG.md",
16
+ "list-contributors": "echo 'clone https://github.com/mgechev/github-contributors-list.git first, then run npm install' && cd ../github-contributors-list && node bin/githubcontrib --owner dherault --repo serverless-offline --sortBy contributions --sortOrder desc > contributors.md",
18
17
  "prepublishOnly": "npm run lint",
18
+ "changelog": "auto-changelog",
19
19
  "prettier": "prettier --check .",
20
20
  "prettier:fix": "prettier --write .",
21
21
  "test": "mocha --require ./tests/mochaHooks.cjs",
@@ -51,72 +51,53 @@
51
51
  "engines": {
52
52
  "node": ">=20.0.0"
53
53
  },
54
- "commit-and-tag-version": {
55
- "skip": {
56
- "commit": true,
57
- "tag": true
58
- },
59
- "types": [
60
- {
61
- "type": "feat",
62
- "section": "Features"
63
- },
64
- {
65
- "type": "fix",
66
- "section": "Bug Fixes"
67
- },
68
- {
69
- "type": "perf",
70
- "section": "Performance Improvements"
71
- },
72
- {
73
- "type": "refactor",
74
- "section": "Maintenance Improvements"
75
- }
76
- ]
54
+ "auto-changelog": {
55
+ "output": "CHANGELOG.md",
56
+ "unreleased": false,
57
+ "commitLimit": false,
58
+ "hideCredit": true
77
59
  },
78
60
  "dependencies": {
79
- "@aws-sdk/client-lambda": "^3.636.0",
61
+ "@aws-sdk/client-lambda": "^3.1052.0",
80
62
  "@hapi/boom": "^10.0.1",
81
63
  "@hapi/h2o2": "^10.0.4",
82
- "@hapi/hapi": "^21.3.10",
64
+ "@hapi/hapi": "^21.4.9",
83
65
  "array-unflat-js": "^0.1.3",
84
66
  "boxen": "^7.1.1",
85
- "chalk": "^5.3.0",
67
+ "chalk": "^5.6.2",
86
68
  "desm": "^1.3.1",
87
69
  "execa": "^8.0.1",
88
- "fs-extra": "^11.2.0",
89
- "is-wsl": "^3.1.0",
70
+ "is-wsl": "^3.1.1",
90
71
  "java-invoke-local": "0.0.6",
91
72
  "jose": "^5.7.0",
92
73
  "js-string-escape": "^1.0.1",
93
- "jsonpath-plus": "^10.2.0",
94
- "jsonschema": "^1.4.1",
74
+ "jsonpath-plus": "^10.4.0",
75
+ "jsonschema": "^1.5.0",
95
76
  "jszip": "^3.10.1",
96
- "luxon": "^3.5.0",
77
+ "luxon": "^3.7.2",
97
78
  "nock": "^13.5.6",
98
79
  "node-fetch": "^3.3.2",
99
80
  "node-schedule": "^2.1.1",
100
81
  "p-memoize": "^7.1.1",
101
82
  "tree-kill": "^1.2.2",
102
- "tsx": "^4.17.0",
103
- "velocityjs": "^2.0.6",
104
- "ws": "^8.18.0"
83
+ "tsx": "^4.22.3",
84
+ "velocityjs": "^2.1.6",
85
+ "ws": "^8.21.0"
105
86
  },
106
87
  "devDependencies": {
107
- "@istanbuljs/esm-loader-hook": "^0.2.0",
88
+ "@istanbuljs/esm-loader-hook": "^0.3.0",
108
89
  "archiver": "^7.0.1",
109
- "commit-and-tag-version": "^12.4.1",
90
+ "auto-changelog": "^2.5.1",
110
91
  "eslint": "^8.57.0",
111
92
  "eslint-config-airbnb-base": "^15.0.0",
112
93
  "eslint-config-prettier": "^9.1.0",
113
- "eslint-plugin-import": "^2.29.1",
114
- "eslint-plugin-prettier": "^5.2.1",
94
+ "eslint-plugin-import": "^2.32.0",
95
+ "eslint-plugin-prettier": "^5.5.5",
115
96
  "eslint-plugin-unicorn": "^54.0.0",
116
- "mocha": "^10.7.3",
97
+ "mocha": "^11.7.6",
117
98
  "nyc": "^17.0.0",
118
- "prettier": "^3.3.3",
119
- "serverless": "^4.2.1"
99
+ "prettier": "^3.8.3",
100
+ "serverless": "^4.36.1"
120
101
  },
121
102
  "peerDependencies": {
122
103
  "serverless": "^4.0.0"
@@ -116,6 +116,11 @@ export default {
116
116
  type: "boolean",
117
117
  usage: "Turns on loading of your HTTP proxy settings from serverless.yml.",
118
118
  },
119
+ rubyWatchDirs: {
120
+ type: "string",
121
+ usage:
122
+ "Comma-separated list of directories to watch for Ruby (.rb) file changes. When set, the persistent Ruby process is automatically restarted on change for hot reload.",
123
+ },
119
124
  terminateIdleLambdaTime: {
120
125
  type: "string",
121
126
  usage:
@@ -23,6 +23,7 @@ export default {
23
23
  preLoadModules: "",
24
24
  reloadHandler: false,
25
25
  resourceRoutes: false,
26
+ rubyWatchDirs: [],
26
27
  terminateIdleLambdaTime: 60,
27
28
  useDocker: false,
28
29
  useInProcess: false,
@@ -24,6 +24,7 @@ export const supportedRuntimesArchitecture = {
24
24
  "ruby2.7": [ARM64, X86_64],
25
25
  "ruby3.2": [ARM64, X86_64],
26
26
  "ruby3.3": [ARM64, X86_64],
27
+ "ruby3.4": [ARM64, X86_64],
27
28
  java8: [X86_64],
28
29
  "java8.al2": [ARM64, X86_64],
29
30
  java11: [ARM64, X86_64],
@@ -68,7 +69,12 @@ export const supportedPython = new Set([
68
69
  ])
69
70
 
70
71
  // RUBY
71
- export const supportedRuby = new Set(["ruby2.7", "ruby3.2", "ruby3.3"])
72
+ export const supportedRuby = new Set([
73
+ "ruby2.7",
74
+ "ruby3.2",
75
+ "ruby3.3",
76
+ "ruby3.4",
77
+ ])
72
78
 
73
79
  export const supportedRuntimes = new Set([
74
80
  // ...supportedDotnetcore,
@@ -294,10 +294,11 @@ export default class HttpServer {
294
294
  }
295
295
 
296
296
  createRoutes(functionKey, albEvent) {
297
- let method = "ANY"
297
+ let methods = ["ANY"]
298
298
  if ((albEvent.conditions.method || []).length > 0) {
299
- method = albEvent.conditions.method[0].toUpperCase()
299
+ methods = albEvent.conditions.method.map((m) => m.toUpperCase())
300
300
  }
301
+ methods = methods.includes("ANY") ? ["ANY"] : methods
301
302
 
302
303
  const path = albEvent.conditions.path[0]
303
304
  const hapiPath = generateAlbHapiPath(path, this.#options, this.#serverless)
@@ -306,51 +307,53 @@ export default class HttpServer {
306
307
  const { host, albPort, httpsProtocol } = this.#options
307
308
  const server = `${httpsProtocol ? "https" : "http"}://${host}:${albPort}`
308
309
 
309
- this.#terminalInfo.push({
310
- invokePath: `/2015-03-31/functions/${functionKey}/invocations`,
311
- method,
312
- path: hapiPath,
313
- server,
314
- stage: this.#options.noPrependStageInUrl ? null : stage,
315
- })
310
+ methods.forEach((method) => {
311
+ this.#terminalInfo.push({
312
+ invokePath: `/2015-03-31/functions/${functionKey}/invocations`,
313
+ method,
314
+ path: hapiPath,
315
+ server,
316
+ stage: this.#options.noPrependStageInUrl ? null : stage,
317
+ })
316
318
 
317
- const hapiMethod = method === "ANY" ? "*" : method
318
- const hapiOptions = {
319
- response: {
320
- emptyStatusCode: 200,
321
- },
322
- }
319
+ const hapiMethod = method === "ANY" ? "*" : method
320
+ const hapiOptions = {
321
+ response: {
322
+ emptyStatusCode: 200,
323
+ },
324
+ }
323
325
 
324
- // skip HEAD routes as hapi will fail with 'Method name not allowed: HEAD ...'
325
- // for more details, check https://github.com/dherault/serverless-offline/issues/204
326
- if (hapiMethod === "HEAD") {
327
- log.notice(
328
- "HEAD method event detected. Skipping HAPI server route mapping",
329
- )
326
+ // skip HEAD routes as hapi will fail with 'Method name not allowed: HEAD ...'
327
+ // for more details, check https://github.com/dherault/serverless-offline/issues/204
328
+ if (hapiMethod === "HEAD") {
329
+ log.notice(
330
+ "HEAD method event detected. Skipping HAPI server route mapping",
331
+ )
330
332
 
331
- return
332
- }
333
+ return
334
+ }
333
335
 
334
- if (hapiMethod !== "HEAD" && hapiMethod !== "GET") {
335
- // maxBytes: Increase request size from 1MB default limit to 10MB.
336
- // Cf AWS API GW payload limits.
337
- hapiOptions.payload = {
338
- maxBytes: 1024 * 1024 * 10,
339
- parse: false,
336
+ if (hapiMethod !== "HEAD" && hapiMethod !== "GET") {
337
+ // maxBytes: Increase request size from 1MB default limit to 10MB.
338
+ // Cf AWS API GW payload limits.
339
+ hapiOptions.payload = {
340
+ maxBytes: 1024 * 1024 * 10,
341
+ parse: false,
342
+ }
340
343
  }
341
- }
342
344
 
343
- const hapiHandler = this.#createHapiHandler({
344
- functionKey,
345
- method,
346
- stage,
347
- })
345
+ const hapiHandler = this.#createHapiHandler({
346
+ functionKey,
347
+ method,
348
+ stage,
349
+ })
348
350
 
349
- this.#server.route({
350
- handler: hapiHandler,
351
- method: hapiMethod,
352
- options: hapiOptions,
353
- path: hapiPath,
351
+ this.#server.route({
352
+ handler: hapiHandler,
353
+ method: hapiMethod,
354
+ options: hapiOptions,
355
+ path: hapiPath,
356
+ })
354
357
  })
355
358
  }
356
359
 
@@ -1,10 +1,9 @@
1
1
  import crypto from "node:crypto"
2
- import { readFile, writeFile } from "node:fs/promises"
2
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises"
3
3
  import { dirname, join, resolve } from "node:path"
4
4
  import process from "node:process"
5
5
  import { performance } from "node:perf_hooks"
6
6
  import { setTimeout } from "node:timers/promises"
7
- import { emptyDir, ensureDir, remove } from "fs-extra"
8
7
  import jszip from "jszip"
9
8
  import { log } from "../utils/log.js"
10
9
  import renderIntrinsicFunction from "../utils/renderIntrinsicFunction.js"
@@ -226,7 +225,7 @@ export default class LambdaFunction {
226
225
  // TODO console.log('lambda cleanup')
227
226
  await this.#handlerRunner.cleanup()
228
227
  if (this.#lambdaDir) {
229
- await remove(this.#lambdaDir)
228
+ await rm(this.#lambdaDir, { force: true, recursive: true })
230
229
  }
231
230
  }
232
231
 
@@ -246,7 +245,8 @@ export default class LambdaFunction {
246
245
  return
247
246
  }
248
247
 
249
- await emptyDir(this.#codeDir)
248
+ await rm(this.#codeDir, { force: true, recursive: true })
249
+ await mkdir(this.#codeDir, { recursive: true })
250
250
 
251
251
  const data = await readFile(this.#artifact)
252
252
  const zip = await jszip.loadAsync(data)
@@ -257,7 +257,7 @@ export default class LambdaFunction {
257
257
  if (filename.endsWith("/")) {
258
258
  return undefined
259
259
  }
260
- await ensureDir(join(this.#codeDir, dirname(filename)))
260
+ await mkdir(join(this.#codeDir, dirname(filename)), { recursive: true })
261
261
  return writeFile(join(this.#codeDir, filename), fileData, {
262
262
  mode: jsZipObj.unixPermissions,
263
263
  })
@@ -52,16 +52,14 @@ export default class HandlerRunner {
52
52
 
53
53
  if (supportedNodejs.has(runtime)) {
54
54
  if (useInProcess) {
55
- const { default: InProcessRunner } = await import(
56
- "./in-process-runner/index.js"
57
- )
55
+ const { default: InProcessRunner } =
56
+ await import("./in-process-runner/index.js")
58
57
 
59
58
  return new InProcessRunner(this.#funOptions, this.#env)
60
59
  }
61
60
 
62
- const { default: WorkerThreadRunner } = await import(
63
- "./worker-thread-runner/index.js"
64
- )
61
+ const { default: WorkerThreadRunner } =
62
+ await import("./worker-thread-runner/index.js")
65
63
 
66
64
  return new WorkerThreadRunner(this.#funOptions, this.#env)
67
65
  }
@@ -81,7 +79,7 @@ export default class HandlerRunner {
81
79
  if (supportedRuby.has(runtime)) {
82
80
  const { default: RubyRunner } = await import("./ruby-runner/index.js")
83
81
 
84
- return new RubyRunner(this.#funOptions, this.#env)
82
+ return new RubyRunner(this.#funOptions, this.#env, this.#options)
85
83
  }
86
84
 
87
85
  if (supportedJava.has(runtime)) {
@@ -1,11 +1,10 @@
1
1
  import { createHash } from "node:crypto"
2
- import { createWriteStream } from "node:fs"
3
- import { readFile, unlink, writeFile } from "node:fs/promises"
2
+ import { createWriteStream, existsSync } from "node:fs"
3
+ import { mkdir, readFile, unlink, writeFile } from "node:fs/promises"
4
4
  import { platform } from "node:os"
5
5
  import { dirname, join, sep } from "node:path"
6
6
  import { LambdaClient, GetLayerVersionCommand } from "@aws-sdk/client-lambda"
7
7
  import { execa } from "execa"
8
- import { ensureDir, pathExists } from "fs-extra"
9
8
  import isWsl from "is-wsl"
10
9
  import jszip from "jszip"
11
10
  import { log, progress } from "../../../utils/log.js"
@@ -103,7 +102,7 @@ export default class DockerContainer {
103
102
 
104
103
  layerDir = join(layerDir, this.#getLayersSha256())
105
104
 
106
- if (await pathExists(layerDir)) {
105
+ if (existsSync(layerDir)) {
107
106
  log.verbose(
108
107
  `Layers already exist for this function. Skipping download.`,
109
108
  )
@@ -143,7 +142,7 @@ export default class DockerContainer {
143
142
  } else {
144
143
  log.debug("Looking for bootstrap file")
145
144
  const bootstrapDir = join(this.#servicePath, "bootstrap")
146
- if (await pathExists(bootstrapDir)) {
145
+ if (existsSync(bootstrapDir)) {
147
146
  log.debug(`Found bootstrap file at ${bootstrapDir}`)
148
147
  dockerArgs.push(
149
148
  "-v",
@@ -262,7 +261,7 @@ export default class DockerContainer {
262
261
  const { CodeSize: layerSize, Location: layerUrl } = layer.Content
263
262
  // const layerSha = layer.Content.CodeSha256
264
263
 
265
- await ensureDir(layerDir)
264
+ await mkdir(layerDir, { recursive: true })
266
265
 
267
266
  log.verbose(
268
267
  `Retrieving "${layerName}": Downloading ${this.#formatBytes(
@@ -311,7 +310,7 @@ export default class DockerContainer {
311
310
  if (filename.endsWith(sep)) {
312
311
  return undefined
313
312
  }
314
- await ensureDir(join(layerDir, dirname(filename)))
313
+ await mkdir(join(layerDir, dirname(filename)), { recursive: true })
315
314
  return writeFile(join(layerDir, filename), fileData, {
316
315
  mode: zip.files[filename].unixPermissions,
317
316
  })