-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(aws-serverless): Replace OTel Lambda instrumentation with handler redirection #22079
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d1a8638
49df016
d6c385a
05894ef
d2514f7
915c495
0e49810
add6c24
d62a433
3b6786d
6919db0
661e3cb
2d5a898
e8d82f7
079361e
3c049fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| // The handler string parsing and module resolution logic in this file is ported from the | ||
| // AWS Lambda runtime interface client: | ||
| // https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/main/src/UserFunction.js | ||
| /* | ||
| * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| import * as fs from 'node:fs'; | ||
| import * as path from 'node:path'; | ||
|
|
||
| export interface ParsedHandler { | ||
| /** Directory portion of the handler string, e.g. `src/` for `src/index.handler`. */ | ||
| moduleRoot: string; | ||
| /** Module file name without extension, e.g. `index` for `src/index.handler`. */ | ||
| moduleName: string; | ||
| /** Export path within the module, e.g. `handler` or `nested.handler`. */ | ||
| functionPath: string; | ||
| } | ||
|
|
||
| /** | ||
| * Parses a Lambda handler string (`<module-root>/<module>.<function-path>`) the same way | ||
| * the AWS Lambda runtime interface client does: the module name is the part of the | ||
| * basename up to the first dot, the function path is everything after it (and may itself | ||
| * contain dots for nested exports). | ||
| * | ||
| * @see https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/main/src/UserFunction.js | ||
| */ | ||
| export function parseHandlerString(handlerString: string): ParsedHandler | undefined { | ||
| const basename = path.basename(handlerString); | ||
| const moduleRoot = handlerString.substring(0, handlerString.length - basename.length); | ||
|
|
||
| const match = basename.match(/^([^.]*)\.(.*)$/); | ||
| if (!match?.[1] || !match[2]) { | ||
| return undefined; | ||
| } | ||
|
|
||
| return { moduleRoot, moduleName: match[1], functionPath: match[2] }; | ||
| } | ||
|
|
||
| export interface ResolvedHandlerFile { | ||
| file: string; | ||
| format: 'cjs' | 'esm'; | ||
| } | ||
|
|
||
| /** | ||
| * Resolves the handler module to a concrete file, mirroring the AWS Lambda runtime's | ||
| * probing order: extensionless file, then `.js` (ESM only when the nearest `package.json` | ||
| * declares `"type": "module"`), then `.mjs`, then `.cjs`. | ||
| * | ||
| * Extensionless files are always CJS: the runtime `require()`s them unconditionally, | ||
| * before its `"type": "module"` check (which only affects `.js` files), and Node cannot | ||
| * `import()` a file without an extension anyway. | ||
| */ | ||
| export function resolveHandlerFile( | ||
| taskRoot: string, | ||
| moduleRoot: string, | ||
| moduleName: string, | ||
| ): ResolvedHandlerFile | undefined { | ||
| const basePath = path.resolve(taskRoot, moduleRoot, moduleName); | ||
|
|
||
| if (isFile(basePath)) { | ||
| return { file: basePath, format: 'cjs' }; | ||
| } | ||
| if (isFile(`${basePath}.js`)) { | ||
| return { file: `${basePath}.js`, format: hasTypeModulePackageJson(path.dirname(basePath)) ? 'esm' : 'cjs' }; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. l: Do lambdas always have a package.json? Or IOW, could we run into issues here where users define an
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that's an issue indeed but that issue also exists for the user, i.e. AWS looks at it the same way. If there's a |
||
| } | ||
| if (isFile(`${basePath}.mjs`)) { | ||
| return { file: `${basePath}.mjs`, format: 'esm' }; | ||
| } | ||
| if (isFile(`${basePath}.cjs`)) { | ||
| return { file: `${basePath}.cjs`, format: 'cjs' }; | ||
| } | ||
|
|
||
| return undefined; | ||
| } | ||
|
|
||
| function isFile(filePath: string): boolean { | ||
| try { | ||
| return fs.statSync(filePath).isFile(); | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Walks up from `dir` looking for the nearest `package.json`, mirroring the AWS runtime's | ||
| * ESM detection. The walk stops at the filesystem root or a `node_modules` boundary. | ||
| */ | ||
| function hasTypeModulePackageJson(dir: string): boolean { | ||
| let current = dir; | ||
| while (true) { | ||
| const packageJsonPath = path.join(current, 'package.json'); | ||
| if (isFile(packageJsonPath)) { | ||
| try { | ||
| const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')) as { type?: string }; | ||
| return packageJson.type === 'module'; | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| const parent = path.dirname(current); | ||
| if (parent === current || path.basename(current) === 'node_modules') { | ||
| return false; | ||
| } | ||
| current = parent; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.