This is an automated email from the ASF dual-hosted git repository.
bsalzano pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-site.git
The following commit(s) were added to refs/heads/main by this push:
new 77cae7d7 docs(chore): add script for runtime dependecies generation
and guides (#61)
77cae7d7 is described below
commit 77cae7d7f8dbbc35ca4faf22a49b48bac47961bb
Author: Antonio Vergari <[email protected]>
AuthorDate: Thu May 8 21:59:01 2025 +0200
docs(chore): add script for runtime dependecies generation and guides (#61)
* docs(chore): add script for runtime dependecies generation
add guide for nodejs, python and php (#98)
* docs(fix): add light mode for table
removed typos on php
add space Creating and invoking PHP actions
add missing php tag
---
README.md | 5 +-
Taskfile.yaml | 11 +
assets/scss/_styles_project.scss | 4 +
content/en/docs/reference/runtimes/_index.md | 66 ++-
.../en/docs/reference/runtimes/nodejs/_index.md | 482 +++++++++++++++++++++
.../reference/runtimes/nodejs/nodejs:v18/_index.md | 21 +
.../reference/runtimes/nodejs/nodejs:v20/_index.md | 21 +
.../reference/runtimes/nodejs/nodejs:v21/_index.md | 21 +
content/en/docs/reference/runtimes/php/_index.md | 148 +++++++
.../docs/reference/runtimes/php/php:v8.0/_index.md | 27 ++
.../docs/reference/runtimes/php/php:v8.1/_index.md | 28 ++
.../docs/reference/runtimes/php/php:v8.2/_index.md | 28 ++
.../docs/reference/runtimes/php/php:v8.3/_index.md | 28 ++
.../en/docs/reference/runtimes/python/_index.md | 138 ++++++
.../runtimes/python/python:v3.10/_index.md | 37 ++
.../runtimes/python/python:v3.11/_index.md | 38 ++
.../runtimes/python/python:v3.12/_index.md | 47 ++
tasks/runtimes/.gitignore | 37 ++
tasks/runtimes/README.md | 15 +
tasks/runtimes/package.json | 17 +
tasks/runtimes/src/index.ts | 77 ++++
tasks/runtimes/src/lib/runtimes.ts | 122 ++++++
tasks/runtimes/src/lib/types.ts | 82 ++++
tasks/runtimes/src/lib/utils.ts | 76 ++++
tasks/runtimes/src/tests/runtimes.test.ts | 0
tasks/runtimes/src/tests/utils.test.ts | 140 ++++++
tasks/runtimes/tsconfig.json | 28 ++
27 files changed, 1734 insertions(+), 10 deletions(-)
diff --git a/README.md b/README.md
index 122e0a36..ce82d734 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,7 @@ The documentation for [OpenServerless
Tasks](https://github.com/apache/openserve
[OpenServerless Tools](https://github.com/apache/openserverless-cli/) is
automatically extracted from the
respective repositories.
-Is possible to align task and tools using these commands:
+Is possible to align task, tools and runtimes using these commands:
- Tasks
- `task import-task`
@@ -50,6 +50,9 @@ Is possible to align task and tools using these commands:
- Tools
- `task import-tools`
+- Runtimes
+ - `task import-runtimes`
+
### Web site build
- Run Hugo Build
diff --git a/Taskfile.yaml b/Taskfile.yaml
index e457f42a..225398ac 100644
--- a/Taskfile.yaml
+++ b/Taskfile.yaml
@@ -42,6 +42,17 @@ tasks:
#cat $file >>content/en/docs/reference/tasks/$tgt
done
- rm -rf openserverless-task
+
+ import-runtimes:
+ desc: import runtimes dependecies
+ silent: true
+ cmds:
+ - test -d "openserverless-task" && git pull || git clone
https://github.com/apache/openserverless-task
+ - test -d "openserverless-runtimes" && git pull || git clone
https://github.com/apache/openserverless-runtimes
+ - |
+ bun tasks/runtimes/src/index.ts
+ - rm -rf openserverless-runtimes
+ - rm -rf openserverless-task
preview:
silent: true
diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss
index 14a0ecfa..859f3e60 100644
--- a/assets/scss/_styles_project.scss
+++ b/assets/scss/_styles_project.scss
@@ -35,6 +35,8 @@
--code-bg-light: #f5f5f5;
--bs-secondary-color: #333333;
--td-pre-bg: #ececec;
+ --bs-body-bg: #ffffff;
+ --bs-emphasis-color: #333333;
}
.dark-mode {
@@ -45,6 +47,8 @@
--code-bg-light: #1e1e1e;
--bs-secondary-color: #e0e0e0;
--td-pre-bg: #1e1e1e;
+ --bs-body-bg: #333333;
+ --bs-emphasis-color: #ffffff;
}
body {
diff --git a/content/en/docs/reference/runtimes/_index.md
b/content/en/docs/reference/runtimes/_index.md
index eebcf819..44cea9f3 100644
--- a/content/en/docs/reference/runtimes/_index.md
+++ b/content/en/docs/reference/runtimes/_index.md
@@ -1,21 +1,69 @@
---
title: Runtimes
-description: List of OpenServerless' supported runtimes
+description:
#weight: 60
draft: false
---
-## Supported Runtimes
-This document is still 🚧 **work in progress** 🚧
+# Apache OpenServerless (OPS) Runtimes Explained
-The programming languages currently directly supported by OpenServerless are:
+Apache OpenServerless (OPS) is a serverless platform built on Apache
OpenWhisk, designed to execute functions in a scalable, event-driven
environment. OPS leverages OpenWhisk's runtime model while extending its
capabilities to support additional customization via Docker.
-- [🚧 Node](actions-nodejs)
+---
+
+## Overview of OPS Runtimes
+
+A **runtime** in OPS is a preconfigured environment that executes a serverless
function.
+Since serverless platforms allocate compute resources dynamically, a runtime
ensures:
+
+1. **Portability** – Developers can write code in different languages without
managing dependencies.
+2. **Scalability** – The system provisions and scales runtimes automatically
based on demand.
+3. **Isolation** – Each Action runs within its own dedicated runtime
environment, ensuring security and consistency.
+4. **Resource Efficiency** – The platform optimizes resource allocation by
suspending inactive runtimes and reusing active ones when possible.
+
+OPS natively supports the following runtimes:
+- **Python**
+- **Node.js**
+- **PHP**
+
+However, since OPS is built on OpenWhisk, it inherits compatibility with all
[OpenWhisk-supported
runtimes](https://openwhisk.apache.org/documentation.html#runtimes) (e.g.,
Java, Go) and allows users to define custom runtimes using Docker containers.
+
+For greater flexibility, developers can also package their own runtime
environments using Docker to create "black box" actions.
+
+---
+
+## How OPS Runtimes Work
+
+### 1. Runtime Lifecycle
+OPS follows OpenWhisk's runtime model, where each function invocation occurs
in an isolated container. The lifecycle includes:
+1. **Initialization**: A container is provisioned with the selected runtime.
+2. **Execution**: The function code runs within the container.
+3. **Idle**: The container is paused (but retained) for reuse (Warm Start).
+4. **Destroying**: Idle containers are garbage-collected after some time.
+
+### 2. Cold vs. Warm Starts
+- **Cold Start**: A new container is created, increasing latency.
+- **Warm Start**: Reuses a paused container for faster execution.
+
+### 3. Runtime Composition
+Each runtime includes:
+- **Language Interpreter/Compiler**: (e.g., Python 3.12, Node.js 21).
+- **Action Interface**: A proxy that implements a canonical protocol to
integrate with the OpenWhisk platform.
+- **Dependencies**: Preinstalled libraries (e.g., `requests` for Python).
+
+---
+
+## Actions
+
+**Actions** are the fundamental execution units in Apache OpenServerless. They
are stateless functions that run on the OpenWhisk platform.
+
+An action can be used to update a database, respond to an API call,
communicate with another system, ecc.
-- [🚧 Python](actions-python)
+To use a function as an action, it must conform to the following:
-- [🚧 Go](actions-go)
+- The function accepts a dictionary as input and produces a dictionary as
output. The input and output dictionaries are
+key-value pairs, where the key is a string and the value is any valid JSON
value. The dictionaries are
+canonically represented as JSON objects when interfacing to an action via the
REST API or the `ops` CLI.
-- [🚧 Java](actions-java)
+- The function must be called `main` or exposed as main
-- [🚧 PHP](actions-php)
diff --git a/content/en/docs/reference/runtimes/nodejs/_index.md
b/content/en/docs/reference/runtimes/nodejs/_index.md
new file mode 100644
index 00000000..2898c77f
--- /dev/null
+++ b/content/en/docs/reference/runtimes/nodejs/_index.md
@@ -0,0 +1,482 @@
+---
+title: NodeJS
+description:
+weight: 50
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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
+#
+# http://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.
+#
+-->
+
+## Creating and invoking JavaScript actions
+
+The process of creating JavaScript actions is similar to that of other actions.
+The following sections guide you through creating and invoking a single
JavaScript action,
+and demonstrate how to bundle multiple JavaScript files and third party
dependencies.
+
+1. Create a package `directory`. Create a JavaScript file with the following
content inside our `packages/nodejs`. For this example, the file name is
`hello.js`.
+
+ ```javascript
+ //--web true
+ //--kind nodejs:default
+ function main() {
+ return { msg: 'Hello world' };
+ }
+ ```
+
+ The JavaScript file might contain additional functions.
+ However, by convention, a function called `main` must exist to provide the
entry point for the action.
+
+ You directory structure should looks like this:
+
+ ```shell
+ nodejs_app
+ └── packages
+ └── nodejs
+ └── hello.js
+
+ ```
+
+2. Create an action from the following JavaScript function. For this example,
the action is called `hello`.
+
+ ```
+ /home/openserverless/.ops/tmp/deploy.pid
+ PID 278075
+ > Scan:
+ >> Action: packages/nodejs/hello.js
+ > Deploying:
+ >> Package: nodejs
+ $ $OPS package update nodejs
+ ok: updated package nodejs
+ >>> Action: packages/nodejs/hello.js
+ $ $OPS action update nodejs/hello packages/nodejs/hello.js --web true --kind
nodejs:default
+ ok: updated action nodejs/hello
+ build process exited with code 0
+ UPLOAD ASSETS FROM web
+ ==================| UPLOAD RESULTS |==================
+ | FILES : 0
+ | COMPLETED : 0
+ | ERRORS : 0
+ | SKIPPED : 0
+ | EXEC. TIME : 2.37 ms
+ ======================================================
+ URL: http://opstutorial.localhost:80
+ ```
+
+ Note: To use a specific version of NodeJs runtime, change the kind property
`--kind nodejs:18`, or `--kind nodejs:20` in the `hello.js` file.
+
+
+
+## Creating asynchronous actions
+
+JavaScript functions that run asynchronously may need to return the activation
result after the `main` function has returned. You can accomplish this by
returning a Promise in your action.
+
+1. Save the following content in a file called `asyncAction.js` inside the
folder `packages/nodejs`.
+
+ ```javascript
+ //--web true
+ //--kind nodejs:default
+ function main(args) {
+ return new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ resolve({ done: true });
+ }, 2000);
+ })
+ }
+ ```
+
+ Notice that the `main` function returns a Promise, which indicates that the
activation hasn't completed yet, but is expected to in the future.
+
+ The `setTimeout()` JavaScript function in this case waits for two seconds
before calling the callback function. This represents the asynchronous code
and goes inside the Promise's callback function.
+
+ The Promise's callback takes two arguments, resolve and reject, which are
both functions. The call to `resolve()` fulfills the Promise and indicates
that the activation has completed normally.
+
+ A call to `reject()` can be used to reject the Promise and signal that the
activation has completed abnormally.
+
+2. Run the following commands to create the action and invoke it:
+
+ ```
+ ops ide deploy
+ ```
+ ```
+ ops action invoke nodejs/asyncAction --result
+ ```
+ ```json
+ {
+ "done": true
+ }
+ ```
+
+ Notice that you performed a blocking invocation of an asynchronous action.
+
+3. Fetch the activation log to see how long the activation took to complete:
+
+ ```
+ ops activation list --limit 1 nodejs/asyncAction
+ ```
+<pre>
+Datetime Activation ID Kind Start Duration
Status Entity
+2024-03-27 19:46:43 64581426b44e4b3d981426b44e3b3d19 nodejs:21 cold 2.033s
success openserverless/asyncAction:0.0.1
+</pre>
+ ```
+ ops activation get 64581426b44e4b3d981426b44e3b3d19
+ ```
+ ```json
+ {
+ ...
+ "start": 1743101268649,
+ "end": 1743101270964,
+ ...
+ }
+ ```
+
+ Comparing the `start` and `end` time stamps in the activation record, you
can see that this activation took slightly over two seconds to complete.
+
+## Using actions to call an external API
+
+The examples so far have been self-contained JavaScript functions. You can
also create an action that calls an external API.
+
+This example invokes a Yahoo Weather service to get the current conditions at
a specific location.
+
+1. Save the following content in a file called `weather.js`.
+
+ ```javascript
+ const fetch = require('node-fetch')
+
+ const getWeatherForecast = async (latitude, longitude) => {
+ const url =
`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=temperature_2m,relative_humidity_2m,wind_speed_10m`
+
+ try {
+ const response = await fetch(url)
+ if (!response.ok) {
+ throw new Error('Error during the request')
+ }
+ const data = await response.json()
+ return data
+ } catch (error) {
+ console.error('Error:', error)
+ return JSON.Stringify(error)
+ }
+ };
+
+ function main(args) {
+ const {latitude, longitude} = args
+
+ return await getWeatherForecast(latitude, longitude)
+ }
+ ```
+
+ Note that the action in the example uses a Node.js library to fetch forecast
data. However, you can use a library available in runtime dependencies or you
can add a package.json file and specify the action's dependencies.
+
+ Check a specific version of the Node.js runtime for packages available in
the runtime environment.
+
+ This example also shows the need for asynchronous actions. The action
returns uses 'async/await' to fetch the data from another system. When the
action is completed, the action will return the values from the other sistem.
+
+2. Create an action from the `weather.js` file:
+
+ ```
+ ops ide deploy
+ ```
+
+3. Use the following command to run the action, and observe the output:
+ ```
+ ops action invoke nodejs/weather --param latitude "51.509865" --param
longitude "-0.118092" --result
+ ```
+
+ Using the `--result` flag means that the value returned from the action is
shown as output on the command-line:
+
+ ```json
+{
+ "elevation": 21,
+ "generationtime_ms": 0.03039836883544922,
+ "hourly": {
+ "temperature_2m": [
+ 12.8,
+ 12.9,
+ ...
+ ],
+ "time": [
+ "2025-03-27T00:00",
+ "2025-03-27T01:00",
+ ...
+ ]
+ },
+ "hourly_units": {
+ "temperature_2m": "°C",
+ "time": "iso8601"
+ },
+ "latitude": 51.5,
+ "longitude": -0.120000124,
+ "timezone": "GMT",
+ "timezone_abbreviation": "GMT",
+ "utc_offset_seconds": 0
+}
+ ```
+
+This example also passed a parameter to the action by using the `--param` flag
and a value that can be changed each time the action is invoked.
+
+## Packaging actions as Node.js modules with NPM libraries
+
+Instead of writing all your action code in a single JavaScript source file,
actions can be deployed from a zip file containing a [Node.js
module](https://nodejs.org/docs/latest-v10.x/api/modules.html#modules_modules).
+
+Archive zip files are extracted into the runtime environment and dynamically
imported using `require()` during initialisation. **Actions packaged as a zip
file MUST contain a valid `package.json` with a `main` field used to denote the
[module index
file](https://nodejs.org/docs/latest-v10.x/api/modules.html#modules_folders_as_modules)
to return.**
+
+`ops ide deploy` will include automatically `node_modules` folder in a zip
file means external NPM libraries can be used on the platform.
+
+Note: remember that each runtime has a set of dependecies already installed.
if it's possible use this set of libraries. It's better to don't load too much
external libraries because it will deteriorate the action's performance
+
+### Simple Example
+
+- Create a folder called `packageAction` inside packages/nodejs folder
+
+- Create the following `package.json` file:
+
+```json
+{
+ "name": "my-action",
+ "main": "index.js",
+ "dependencies" : {
+ "left-pad" : "1.1.3"
+ }
+}
+```
+
+- Create the following `index.js` file:
+
+```javascript
+//--web true
+//--kind nodejs:default
+const leftPad = require("left-pad")
+function main (args) {
+ const lines = args.lines || [];
+ return { padded: lines.map(l => leftPad(l, 30, ".")) }
+}
+```
+
+- Now you should have this folder structure:
+
+ ```shell
+ nodejs_app
+ └── packages
+ └── nodejs
+ └── packageAction
+ ├── hello.js
+ └── package.json
+ ```
+
+- Create the action
+
+```
+ops ide deploy
+```
+
+When creating an action from a folder, `ops ide deploy` will create
automatically the following artifacts:
+- node_modules
+- package-lock.json
+- \<foldername\>.zip
+
+the zip fill is used by `ops` to create/update the function in your ops
environment
+
+- Invoke the action as normal.
+
+```
+ops action invoke nodejs/packageAction --param lines "[\"and now\", \"for
something completely\", \"different\" ]" --result
+```
+```json
+{
+ "padded": [
+ ".......................and now",
+ "......for something completely",
+ ".....................different"
+ ]
+}
+```
+
+## Using JavaScript Bundlers to package action source files
+
+Using a JavaScript module bundler can transform application source files (with
external dependencies) into a single compressed JavaScript file. This can lead
to faster deployments, lower cold-starts and allow you to deploy large
applications where individual sources files in a zip archive are larger than
the action size limit.
+
+Here are the instructions for how to use three popular module bundlers with
the Node.js runtime. The "left pad" action example will be used as the source
file for bundling along with the external library.
+
+### Using rollup.js
+
+In this example we will use [rollupjs](https://rollupjs.org) to deliver the
action
+
+- Create rollupAction folder inside packages/nodejs. Then re-write the
`index.js` to use ES6 Modules, rather than CommonJS module format.
+Create the `package.json` and copy the content from the previous example
+
+
+```javascript
+import leftPad from 'left-pad';
+
+function myAction(args) {
+ const lines = args.lines || [];
+ return { padded: lines.map(l => leftPad(l, 30, ".")) }
+}
+
+export const main = myAction
+```
+
+*Make sure you export the function using the `const main = ...` pattern. Using
`export {myAction as main}` does not work due to tree-shaking. See this [blog
post](https://boneskull.com/rollup-for-javascript-actions-on-openwhisk/) for
full details on why this is necessary.*
+
+- Create the Rollup.js configuration file in `rollup.config.js` with the
following contents.
+
+```javascript
+import commonjs from 'rollup-plugin-commonjs';
+import resolve from 'rollup-plugin-node-resolve';
+
+export default {
+ input: 'index.js',
+ output: {
+ file: 'bundle.js',
+ format: 'cjs'
+ },
+ plugins: [
+ resolve(),
+ commonjs()
+ ]
+};
+```
+
+Note: run the following command inside the rollupAction folder
+
+- Install the Rollup.js library and plugins using NPM.
+
+```
+npm install rollup rollup-plugin-commonjs rollup-plugin-node-resolve
--save-dev
+```
+
+- Run the Rollup.js tool using the configuration file.
+
+```
+npx rollup --config
+```
+
+- Create an action using the bundle source file.
+
+```
+ops action create nodejs/rollupAction bundle.js --kind nodejs:20
+```
+
+- Invoke the action as normal. Results should be the same as the example above.
+
+```
+ops action invoke nodejs/rollupAction --result --param lines "[\"and now\",
\"for something completely\", \"different\" ]"
+```
+
+### Using webpack
+
+In this example we will use [webpack](https://webpack.js.org/) to deliver the
action
+
+- Create webpackAction folder inside packages/nodejs. Then re-write the
`index.js` to export the `main` function using as a global reference.
+ Then, create the `package.json` and copy the content from the previous
example
+
+
+```javascript
+const leftPad = require('left-pad');
+
+function myAction(args) {
+ const lines = args.lines || [];
+ return { padded: lines.map(l => leftPad(l, 30, ".")) }
+}
+
+global.main = myAction
+```
+
+This allows the bundle source to "break out" of the closures Webpack uses when
defining the modules.
+
+- Create the Webpack configuration file in `webpack.config.js` with the
following contents.
+
+```javascript
+module.exports = {
+ entry: './index.js',
+ target: 'node',
+ output: {
+ filename: 'bundle.js'
+ }
+};
+```
+
+- Install the Webpack library and CLI using NPM.
+
+```
+npm install webpack-cli --save-dev
+```
+
+- Run the Webpack tool using the configuration file.
+
+```
+npx webpack --config webpack.config.js
+```
+
+- Create an action using the bundle source file.
+
+```
+ops action create nodejs/webpackAction dist/bundle.js --kind nodejs:21
+```
+
+- Invoke the action as normal. Results should be the same as the example above.
+
+```
+ops action invoke nodejs/webpackAction --result --param lines "[\"and now\",
\"for something completely\", \"different\" ]"
+```
+
+<!-- ### Using parcel ([https://parceljs.org/](https://parceljs.org/))
+
+In this example we will use [parceljs](https://parceljs.org) to deliver the
action
+
+- Create parcelAction folder inside packages/nodejs. Create the `package.json`
and copy the content from the previous example
+ Then, create the `index.js` and export the `main` function using as a global
reference.
+
+```javascript
+const leftPad = require('left-pad');
+
+function myAction(args) {
+ const lines = args.lines || [];
+ return { padded: lines.map(l => leftPad(l, 30, ".")) }
+}
+
+global.main = myAction
+```
+
+This allows the bundle source to "break out" of the closures Parcel uses when
defining the modules.
+
+- Install the Parcel library using NPM.
+
+```
+npm install parcel-bundler --save-dev
+```
+
+- Run the Parcel tool using the configuration file.
+
+```
+ npx parcel index.js
+```
+
+- Create an action using the bundle source file.
+
+```
+ops action create nodejs/parcelAction dist/index.js --kind nodejs:21
+```
+
+- Invoke the action as normal. Results should be the same as the example above.
+
+```
+ops action invoke nodejs/parcelAction --result --param lines "[\"and now\",
\"for something completely\", \"different\" ]"
+``` -->
+
diff --git a/content/en/docs/reference/runtimes/nodejs/nodejs:v18/_index.md
b/content/en/docs/reference/runtimes/nodejs/nodejs:v18/_index.md
new file mode 100644
index 00000000..5a1d711b
--- /dev/null
+++ b/content/en/docs/reference/runtimes/nodejs/nodejs:v18/_index.md
@@ -0,0 +1,21 @@
+---
+title: nodejs:v18
+description:
+weight: 50
+---
+## nodejs:v18 (default: false)
+
+| Library | Version | Link |
+|------------|---------|------|
+| @azure/openai | ^1.0.0-beta.11 |
[npm](https://npmjs.com/package/v/1.0.0-beta.11) |
+| @langchain/community | ^0.0.34 | [npm](https://npmjs.com/package/v/0.0.34) |
+| minio | ^7.1.3 | [npm](https://npmjs.com/package/v/7.1.3) |
+| mongodb | ^6.4.0 | [npm](https://npmjs.com/package/v/6.4.0) |
+| node-auth0 | ^1.0.0 | [npm](https://npmjs.com/package/v/1.0.0) |
+| ollama | ^0.5.0 | [npm](https://npmjs.com/package/v/0.5.0) |
+| openai | ^4.28.4 | [npm](https://npmjs.com/package/v/4.28.4) |
+| openwhisk | ^3.21.8 | [npm](https://npmjs.com/package/v/3.21.8) |
+| pg | ^8.11.3 | [npm](https://npmjs.com/package/v/8.11.3) |
+| plotly | ^1.0.6 | [npm](https://npmjs.com/package/v/1.0.6) |
+| redis | ^4.6.13 | [npm](https://npmjs.com/package/v/4.6.13) |
+| uuid | ^9.0.1 | [npm](https://npmjs.com/package/v/9.0.1) |
diff --git a/content/en/docs/reference/runtimes/nodejs/nodejs:v20/_index.md
b/content/en/docs/reference/runtimes/nodejs/nodejs:v20/_index.md
new file mode 100644
index 00000000..0b13af49
--- /dev/null
+++ b/content/en/docs/reference/runtimes/nodejs/nodejs:v20/_index.md
@@ -0,0 +1,21 @@
+---
+title: nodejs:v20
+description:
+weight: 50
+---
+## nodejs:v20 (default: false)
+
+| Library | Version | Link |
+|------------|---------|------|
+| @azure/openai | ^1.0.0-beta.11 |
[npm](https://npmjs.com/package/v/1.0.0-beta.11) |
+| @langchain/community | ^0.0.34 | [npm](https://npmjs.com/package/v/0.0.34) |
+| minio | ^7.1.3 | [npm](https://npmjs.com/package/v/7.1.3) |
+| mongodb | ^6.4.0 | [npm](https://npmjs.com/package/v/6.4.0) |
+| node-auth0 | ^1.0.0 | [npm](https://npmjs.com/package/v/1.0.0) |
+| ollama | ^0.5.0 | [npm](https://npmjs.com/package/v/0.5.0) |
+| openai | ^4.28.4 | [npm](https://npmjs.com/package/v/4.28.4) |
+| openwhisk | ^3.21.8 | [npm](https://npmjs.com/package/v/3.21.8) |
+| pg | ^8.11.3 | [npm](https://npmjs.com/package/v/8.11.3) |
+| plotly | ^1.0.6 | [npm](https://npmjs.com/package/v/1.0.6) |
+| redis | ^4.6.13 | [npm](https://npmjs.com/package/v/4.6.13) |
+| uuid | ^9.0.1 | [npm](https://npmjs.com/package/v/9.0.1) |
diff --git a/content/en/docs/reference/runtimes/nodejs/nodejs:v21/_index.md
b/content/en/docs/reference/runtimes/nodejs/nodejs:v21/_index.md
new file mode 100644
index 00000000..d0d6fdfc
--- /dev/null
+++ b/content/en/docs/reference/runtimes/nodejs/nodejs:v21/_index.md
@@ -0,0 +1,21 @@
+---
+title: nodejs:v21
+description:
+weight: 50
+---
+## nodejs:v21 (default: true)
+
+| Library | Version | Link |
+|------------|---------|------|
+| @azure/openai | ^1.0.0-beta.11 |
[npm](https://npmjs.com/package/v/1.0.0-beta.11) |
+| @langchain/community | ^0.0.34 | [npm](https://npmjs.com/package/v/0.0.34) |
+| minio | ^7.1.3 | [npm](https://npmjs.com/package/v/7.1.3) |
+| mongodb | ^6.4.0 | [npm](https://npmjs.com/package/v/6.4.0) |
+| node-auth0 | ^1.0.0 | [npm](https://npmjs.com/package/v/1.0.0) |
+| ollama | ^0.5.0 | [npm](https://npmjs.com/package/v/0.5.0) |
+| openai | ^4.28.4 | [npm](https://npmjs.com/package/v/4.28.4) |
+| openwhisk | ^3.21.8 | [npm](https://npmjs.com/package/v/3.21.8) |
+| pg | ^8.11.3 | [npm](https://npmjs.com/package/v/8.11.3) |
+| plotly | ^1.0.6 | [npm](https://npmjs.com/package/v/1.0.6) |
+| redis | ^4.6.13 | [npm](https://npmjs.com/package/v/4.6.13) |
+| uuid | ^9.0.1 | [npm](https://npmjs.com/package/v/9.0.1) |
diff --git a/content/en/docs/reference/runtimes/php/_index.md
b/content/en/docs/reference/runtimes/php/_index.md
new file mode 100644
index 00000000..6e4691dc
--- /dev/null
+++ b/content/en/docs/reference/runtimes/php/_index.md
@@ -0,0 +1,148 @@
+---
+title: PHP
+description:
+weight: 50
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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
+#
+# http://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.
+#
+-->
+
+## Creating and invoking PHP actions
+
+The process of creating PHP actions is similar to that of other actions.
+The following sections guide you through creating and invoking a single PHP
action,
+and demonstrate how to bundle multiple PHP files and third party dependencies.
+
+1. Create a Python_app folder and then create a `package` directory.Now create
a Python file with the following content inside our `packages/php`. For this
example, the file name is `hello.php`.
+
+ ```php
+<?php
+//--web true
+//--kind php:default
+function main(array $args) : array
+{
+ $name = $args["name"] ?? "stranger";
+ $greeting = "Hello $name!";
+ echo $greeting;
+ return ["greeting" => $greeting];
+}
+?>
+
+ ```
+
+ The PHP file might contain additional functions.
+ However, by convention, a function called `main` must exist to provide the
entry point for the action.
+
+ Your directory structure should looks like this:
+
+ ```shell
+ PHP_app
+ └── packages
+ └── php
+ └── hello.php
+
+ ```
+2. Run the following command to deploy the action
+```bash
+ops ide deploy
+```
+
+3. Ops will create the action automatically. For this example, the action is
called `php/hello`.
+
+ ```
+ /home/openserverless.ops/tmp/deploy.pid
+ PID 220917
+ > Scan:
+ >> Action: packages/php/hello.php
+ > Deploying:
+ >> Package: php
+ $ $OPS package update php
+ ok: updated package php
+ >>> Action: packages/php/hello.php
+ $ $OPS action update php/hello packages/php/hello.php
+ ok: updated action php/hello
+ build process exited with code 0
+ UPLOAD ASSETS FROM web
+ ==================| UPLOAD RESULTS |==================
+ | FILES : 0
+ | COMPLETED : 0
+ | ERRORS : 0
+ | SKIPPED : 0
+ | EXEC. TIME : 2.61 ms
+ ======================================================
+ URL: http://openserverless.localhost:80
+ ```
+
+ Note: To use a specific version of Python runtime, change the kind property
`--kind php:8.1`, or `--kind php:8.3` in the `hello.py` file.
+
+4. To invoke the action run the following command:
+
+```bash
+ops action invoke php/hello --param name Marina --result
+```
+
+<!-- ## Packaging php actions in zip files
+TODO: convert code for PHP
+Sometimes you action would be more complex and probably you prefer to create
multiple file in order to organize better your code.
+
+In this example we're creating a more complex php action.
+
+1. Create a folder `complex_action` inside `packages/php` folder. Then create
two php files
+
+index.php
+ ```php
+<?php
+//--web true
+//--kind php:default
+include 'utils.php';
+
+function main($args) {
+ $name = isset($args['name']) ? $args['name'] : 'stranger';
+ $result = concat_string('Nice to meet you,', $name);
+ return array('greeting' => $result);
+}
+?>
+
+ ```
+
+utils.php
+ ```php
+<?php
+
+function concat_string($first_string, $second_string) {
+ return "$first_string $second_string";
+}
+?>
+ ```
+
+Note: The filename of the source file containing the entry point (e.g.,
`main`) must be `index.php`.
+
+2. Deploy the action using `ide deploy`
+
+```bash
+ops ide deploy
+```
+
+3. Now you can invoke your action. In this case the action name is
`php/complex_action`
+
+```bash
+ops action invoke php/complex_action --param name Marina --result
+```
+ -->
+
diff --git a/content/en/docs/reference/runtimes/php/php:v8.0/_index.md
b/content/en/docs/reference/runtimes/php/php:v8.0/_index.md
new file mode 100644
index 00000000..3f3a12a2
--- /dev/null
+++ b/content/en/docs/reference/runtimes/php/php:v8.0/_index.md
@@ -0,0 +1,27 @@
+---
+title: php:v8.0
+description:
+weight: 50
+---
+## php:v8.0 (default: false)
+
+| Library | Version | Link |
+|------------|---------|------|
+| guzzlehttp/guzzle | 7.2.0 |
[packagist](https://packagist.org/packages/guzzlehttp/guzzle#7.2.0) |
+| ramsey/uuid | 4.1.1 |
[packagist](https://packagist.org/packages/ramsey/uuid#4.1.1) |
+| aws/aws-sdk-php | 3.209.31 \|\| 3.210.0 |
[packagist](https://packagist.org/packages/aws/aws-sdk-php#3.209.31 ) |
+| bcmath | Latest* | [PHP.net](https://www.php.net/manual/en/book.bcmath.php)
|
+| gd | Latest* | [PHP.net](https://www.php.net/manual/en/book.image.php) |
+| intl | Latest* | [PHP.net](https://www.php.net/manual/en/book.intl.php) |
+| mysqli | Latest* | [PHP.net](https://www.php.net/manual/en/book.mysqli.php)
|
+| mongodb | Latest* | [PHP.net](https://pecl.php.net/package/mongodb) |
+| opcache | Latest* |
[PHP.net](https://www.php.net/manual/en/book.opcache.php) |
+| pgsql | Latest* | [PHP.net](https://www.php.net/manual/en/book.pgsql.php) |
+| pdo_mysql | Latest* |
[PHP.net](https://www.php.net/manual/en/ref.pdo-mysql.php) |
+| pdo_pgsql | Latest* |
[PHP.net](https://www.php.net/manual/en/ref.pdo-pgsql.php) |
+| redis | Latest* | [PHP.net](https://pecl.php.net/package/redis) |
+| soap | Latest* | [PHP.net](https://www.php.net/manual/en/book.soap.php) |
+| zip | Latest* | [PHP.net](https://www.php.net/manual/en/book.zip.php) |
+
+
+ *Latest version available for current runtime version
\ No newline at end of file
diff --git a/content/en/docs/reference/runtimes/php/php:v8.1/_index.md
b/content/en/docs/reference/runtimes/php/php:v8.1/_index.md
new file mode 100644
index 00000000..ada82692
--- /dev/null
+++ b/content/en/docs/reference/runtimes/php/php:v8.1/_index.md
@@ -0,0 +1,28 @@
+---
+title: php:v8.1
+description:
+weight: 50
+---
+## php:v8.1 (default: false)
+
+| Library | Version | Link |
+|------------|---------|------|
+| guzzlehttp/guzzle | 7.4.5 |
[packagist](https://packagist.org/packages/guzzlehttp/guzzle#7.4.5) |
+| ramsey/uuid | 4.4.0 |
[packagist](https://packagist.org/packages/ramsey/uuid#4.4.0) |
+| openai-php/client | v0.8.5 |
[packagist](https://packagist.org/packages/openai-php/client#v0.8.5) |
+| aws/aws-sdk-php | 3.306.7 |
[packagist](https://packagist.org/packages/aws/aws-sdk-php#3.306.7) |
+| bcmath | Latest* | [PHP.net](https://www.php.net/manual/en/book.bcmath.php)
|
+| gd | Latest* | [PHP.net](https://www.php.net/manual/en/book.image.php) |
+| intl | Latest* | [PHP.net](https://www.php.net/manual/en/book.intl.php) |
+| mysqli | Latest* | [PHP.net](https://www.php.net/manual/en/book.mysqli.php)
|
+| mongodb | Latest* | [PHP.net](https://pecl.php.net/package/mongodb) |
+| opcache | Latest* |
[PHP.net](https://www.php.net/manual/en/book.opcache.php) |
+| pgsql | Latest* | [PHP.net](https://www.php.net/manual/en/book.pgsql.php) |
+| pdo_mysql | Latest* |
[PHP.net](https://www.php.net/manual/en/ref.pdo-mysql.php) |
+| pdo_pgsql | Latest* |
[PHP.net](https://www.php.net/manual/en/ref.pdo-pgsql.php) |
+| redis | Latest* | [PHP.net](https://pecl.php.net/package/redis) |
+| soap | Latest* | [PHP.net](https://www.php.net/manual/en/book.soap.php) |
+| zip | Latest* | [PHP.net](https://www.php.net/manual/en/book.zip.php) |
+
+
+ *Latest version available for current runtime version
\ No newline at end of file
diff --git a/content/en/docs/reference/runtimes/php/php:v8.2/_index.md
b/content/en/docs/reference/runtimes/php/php:v8.2/_index.md
new file mode 100644
index 00000000..1af5b63c
--- /dev/null
+++ b/content/en/docs/reference/runtimes/php/php:v8.2/_index.md
@@ -0,0 +1,28 @@
+---
+title: php:v8.2
+description:
+weight: 50
+---
+## php:v8.2 (default: false)
+
+| Library | Version | Link |
+|------------|---------|------|
+| guzzlehttp/guzzle | 7.7.0 |
[packagist](https://packagist.org/packages/guzzlehttp/guzzle#7.7.0) |
+| ramsey/uuid | 4.7.4 |
[packagist](https://packagist.org/packages/ramsey/uuid#4.7.4) |
+| openai-php/client | v0.8.5 |
[packagist](https://packagist.org/packages/openai-php/client#v0.8.5) |
+| aws/aws-sdk-php | 3.306.7 |
[packagist](https://packagist.org/packages/aws/aws-sdk-php#3.306.7) |
+| bcmath | Latest* | [PHP.net](https://www.php.net/manual/en/book.bcmath.php)
|
+| gd | Latest* | [PHP.net](https://www.php.net/manual/en/book.image.php) |
+| intl | Latest* | [PHP.net](https://www.php.net/manual/en/book.intl.php) |
+| mysqli | Latest* | [PHP.net](https://www.php.net/manual/en/book.mysqli.php)
|
+| mongodb | Latest* | [PHP.net](https://pecl.php.net/package/mongodb) |
+| opcache | Latest* |
[PHP.net](https://www.php.net/manual/en/book.opcache.php) |
+| pgsql | Latest* | [PHP.net](https://www.php.net/manual/en/book.pgsql.php) |
+| pdo_mysql | Latest* |
[PHP.net](https://www.php.net/manual/en/ref.pdo-mysql.php) |
+| pdo_pgsql | Latest* |
[PHP.net](https://www.php.net/manual/en/ref.pdo-pgsql.php) |
+| redis | Latest* | [PHP.net](https://pecl.php.net/package/redis) |
+| soap | Latest* | [PHP.net](https://www.php.net/manual/en/book.soap.php) |
+| zip | Latest* | [PHP.net](https://www.php.net/manual/en/book.zip.php) |
+
+
+ *Latest version available for current runtime version
\ No newline at end of file
diff --git a/content/en/docs/reference/runtimes/php/php:v8.3/_index.md
b/content/en/docs/reference/runtimes/php/php:v8.3/_index.md
new file mode 100644
index 00000000..7d90c9d7
--- /dev/null
+++ b/content/en/docs/reference/runtimes/php/php:v8.3/_index.md
@@ -0,0 +1,28 @@
+---
+title: php:v8.3
+description:
+weight: 50
+---
+## php:v8.3 (default: true)
+
+| Library | Version | Link |
+|------------|---------|------|
+| guzzlehttp/guzzle | 7.8.1 |
[packagist](https://packagist.org/packages/guzzlehttp/guzzle#7.8.1) |
+| ramsey/uuid | 4.7.5 |
[packagist](https://packagist.org/packages/ramsey/uuid#4.7.5) |
+| openai-php/client | v0.8.5 |
[packagist](https://packagist.org/packages/openai-php/client#v0.8.5) |
+| aws/aws-sdk-php | 3.306.7 |
[packagist](https://packagist.org/packages/aws/aws-sdk-php#3.306.7) |
+| bcmath | Latest* | [PHP.net](https://www.php.net/manual/en/book.bcmath.php)
|
+| gd | Latest* | [PHP.net](https://www.php.net/manual/en/book.image.php) |
+| intl | Latest* | [PHP.net](https://www.php.net/manual/en/book.intl.php) |
+| mysqli | Latest* | [PHP.net](https://www.php.net/manual/en/book.mysqli.php)
|
+| mongodb | Latest* | [PHP.net](https://pecl.php.net/package/mongodb) |
+| opcache | Latest* |
[PHP.net](https://www.php.net/manual/en/book.opcache.php) |
+| pgsql | Latest* | [PHP.net](https://www.php.net/manual/en/book.pgsql.php) |
+| pdo_mysql | Latest* |
[PHP.net](https://www.php.net/manual/en/ref.pdo-mysql.php) |
+| pdo_pgsql | Latest* |
[PHP.net](https://www.php.net/manual/en/ref.pdo-pgsql.php) |
+| redis | Latest* | [PHP.net](https://pecl.php.net/package/redis) |
+| soap | Latest* | [PHP.net](https://www.php.net/manual/en/book.soap.php) |
+| zip | Latest* | [PHP.net](https://www.php.net/manual/en/book.zip.php) |
+
+
+ *Latest version available for current runtime version
\ No newline at end of file
diff --git a/content/en/docs/reference/runtimes/python/_index.md
b/content/en/docs/reference/runtimes/python/_index.md
new file mode 100644
index 00000000..50ec9782
--- /dev/null
+++ b/content/en/docs/reference/runtimes/python/_index.md
@@ -0,0 +1,138 @@
+---
+title: Python
+description:
+weight: 50
+---
+
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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
+#
+# http://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.
+#
+-->
+
+## Creating and invoking Python actions
+
+The process of creating Python actions is similar to that of other actions.
+The following sections guide you through creating and invoking a single Python
action,
+and demonstrate how to bundle multiple Python files and third party
dependencies.
+
+1. Create a Python_app folder and then create a `package` directory.Now create
a Python file with the following content inside our `packages/python`. For this
example, the file name is `hello.py`.
+
+ ```Python
+#--web true
+#--kind python:default
+
+def main(args):
+ name = args.get("name", "stranger")
+ result = f"Hello {name}!"
+ print(result)
+ return {"greeting": result}
+
+ ```
+
+ The Python file might contain additional functions.
+ However, by convention, a function called `main` must exist to provide the
entry point for the action.
+
+ Your directory structure should looks like this:
+
+ ```shell
+ Python_app
+ └── packages
+ └── Python
+ └── hello.py
+
+ ```
+2. Run the following command to deploy the action
+```bash
+ops ide deploy
+```
+
+3. Ops will create the action automatically. For this example, the action is
called `python/hello`.
+
+ ```
+ /home/openserverless/.ops/tmp/deploy.pid
+ PID 278075
+ > Scan:
+ >> Action: packages/python/hello.py
+ > Deploying:
+ >> Package: python
+ $ $OPS package update python
+ ok: updated package python
+ >>> Action: packages/python/hello.py
+ $ $OPS action update python/hello packages/python/hello.py --web true --kind
python:default
+ ok: updated action python/hello
+ build process exited with code 0
+ UPLOAD ASSETS FROM web
+ ==================| UPLOAD RESULTS |==================
+ | FILES : 0
+ | COMPLETED : 0
+ | ERRORS : 0
+ | SKIPPED : 0
+ | EXEC. TIME : 2.37 ms
+ ======================================================
+ URL: http://opstutorial.localhost:80
+ ```
+
+ Note: To use a specific version of Python runtime, change the kind property
`--kind python:3.11`, or `--kind python:3.12` in the `hello.py` file.
+
+4. To invoke the action run the following command:
+
+```bash
+ops action invoke python/hello --param name Marina --result
+```
+
+## Packaging Python actions in zip files
+
+Sometimes you action would be more complex and probably you prefer to create
multiple file in order to organize better your code.
+
+In this example we're creating a more complex python action.
+
+1. Create a folder `complex_action` inside `packages/python` folder. Then
create two python files
+
+__main__.py
+ ```Python
+#--web true
+#--kind python:default
+import utils
+
+def main(args):
+ name = args.get("name", "stranger")
+ result = utils.concat_string("Nice to meet you,",name)
+ return {"greeting": result}
+
+ ```
+
+utils.py
+ ```Python
+def concat_string(first_string: str, second_string: str):
+ return f"{first_string} {second_string}"
+ ```
+
+Note: The filename of the source file containing the entry point (e.g.,
`main`) must be `__main__.py`.
+
+2. Deploy the action using `ide deploy`
+
+```bash
+ops ide deploy
+```
+
+3. Now you can invoke your action. In this case the action name is
`python/complex_action`
+
+```bash
+ops action invoke python/complex_action --param name Marina --result
+```
+
diff --git a/content/en/docs/reference/runtimes/python/python:v3.10/_index.md
b/content/en/docs/reference/runtimes/python/python:v3.10/_index.md
new file mode 100644
index 00000000..2dd50c70
--- /dev/null
+++ b/content/en/docs/reference/runtimes/python/python:v3.10/_index.md
@@ -0,0 +1,37 @@
+---
+title: python:v3.10
+description:
+weight: 50
+---
+## python:v3.10 (default: false)
+
+| Library | Version | Link |
+|------------|---------|------|
+| beautifulsoup4 | 4.10.0 |
[PyPI](https://pypi.org/project/beautifulsoup4/4.10.0) |
+| httplib2 | 0.19.1 | [PyPI](https://pypi.org/project/httplib2/0.19.1) |
+| kafka_python | 2.0.2 | [PyPI](https://pypi.org/project/kafka_python/2.0.2) |
+| python-dateutil | 2.8.2 |
[PyPI](https://pypi.org/project/python-dateutil/2.8.2) |
+| requests | 2.31.0 | [PyPI](https://pypi.org/project/requests/2.31.0) |
+| scrapy | 2.5.0 | [PyPI](https://pypi.org/project/scrapy/2.5.0) |
+| simplejson | 3.17.5 | [PyPI](https://pypi.org/project/simplejson/3.17.5) |
+| twisted | 21.7.0 | [PyPI](https://pypi.org/project/twisted/21.7.0) |
+| netifaces | 0.11.0 | [PyPI](https://pypi.org/project/netifaces/0.11.0) |
+| pyyaml | 6.0 | [PyPI](https://pypi.org/project/pyyaml/6.0) |
+| redis | 4.4.2 | [PyPI](https://pypi.org/project/redis/4.4.2) |
+| boto3 | 1.28.25 | [PyPI](https://pypi.org/project/boto3/1.28.25) |
+| psycopg | 3.1.10 | [PyPI](https://pypi.org/project/psycopg/3.1.10) |
+| pymongo | 4.4.1 | [PyPI](https://pypi.org/project/pymongo/4.4.1) |
+| minio | 7.1.16 | [PyPI](https://pypi.org/project/minio/7.1.16) |
+| openai | 1.7.1 | [PyPI](https://pypi.org/project/openai/1.7.1) |
+| auth0-python | 4.6.0 | [PyPI](https://pypi.org/project/auth0-python/4.6.0) |
+| langchain | 0.1.0 | [PyPI](https://pypi.org/project/langchain/0.1.0) |
+| farm-haystack | 1.23.0 |
[PyPI](https://pypi.org/project/farm-haystack/1.23.0) |
+| nltk | 3.8.1 | [PyPI](https://pypi.org/project/nltk/3.8.1) |
+| langdetect | 1.0.9 | [PyPI](https://pypi.org/project/langdetect/1.0.9) |
+| ollama | 0.1.9 | [PyPI](https://pypi.org/project/ollama/0.1.9) |
+| joblib | 1.4.2 | [PyPI](https://pypi.org/project/joblib/1.4.2) |
+| lightgbm | 4.5.0 | [PyPI](https://pypi.org/project/lightgbm/4.5.0) |
+| feedparser | 6.0.11 | [PyPI](https://pypi.org/project/feedparser/6.0.11) |
+| numpy | 1.26.4 | [PyPI](https://pypi.org/project/numpy/1.26.4) |
+| scikit-learn | 1.5.2 | [PyPI](https://pypi.org/project/scikit-learn/1.5.2) |
+| bcrypt | 4.2.1 | [PyPI](https://pypi.org/project/bcrypt/4.2.1) |
diff --git a/content/en/docs/reference/runtimes/python/python:v3.11/_index.md
b/content/en/docs/reference/runtimes/python/python:v3.11/_index.md
new file mode 100644
index 00000000..94478b0e
--- /dev/null
+++ b/content/en/docs/reference/runtimes/python/python:v3.11/_index.md
@@ -0,0 +1,38 @@
+---
+title: python:v3.11
+description:
+weight: 50
+---
+## python:v3.11 (default: false)
+
+| Library | Version | Link |
+|------------|---------|------|
+| beautifulsoup4 | 4.10.0 |
[PyPI](https://pypi.org/project/beautifulsoup4/4.10.0) |
+| httplib2 | 0.19.1 | [PyPI](https://pypi.org/project/httplib2/0.19.1) |
+| kafka_python | 2.0.2 | [PyPI](https://pypi.org/project/kafka_python/2.0.2) |
+| python-dateutil | 2.8.2 |
[PyPI](https://pypi.org/project/python-dateutil/2.8.2) |
+| requests | 2.31.0 | [PyPI](https://pypi.org/project/requests/2.31.0) |
+| scrapy | 2.5.0 | [PyPI](https://pypi.org/project/scrapy/2.5.0) |
+| simplejson | 3.17.5 | [PyPI](https://pypi.org/project/simplejson/3.17.5) |
+| twisted | 21.7.0 | [PyPI](https://pypi.org/project/twisted/21.7.0) |
+| netifaces | 0.11.0 | [PyPI](https://pypi.org/project/netifaces/0.11.0) |
+| pyyaml | 6.0 | [PyPI](https://pypi.org/project/pyyaml/6.0) |
+| redis | 4.4.2 | [PyPI](https://pypi.org/project/redis/4.4.2) |
+| boto3 | 1.28.25 | [PyPI](https://pypi.org/project/boto3/1.28.25) |
+| psycopg | 3.1.10 | [PyPI](https://pypi.org/project/psycopg/3.1.10) |
+| pymongo | 4.4.1 | [PyPI](https://pypi.org/project/pymongo/4.4.1) |
+| minio | 7.1.16 | [PyPI](https://pypi.org/project/minio/7.1.16) |
+| openai | 1.7.1 | [PyPI](https://pypi.org/project/openai/1.7.1) |
+| auth0-python | 4.6.0 | [PyPI](https://pypi.org/project/auth0-python/4.6.0) |
+| langchain | 0.1.0 | [PyPI](https://pypi.org/project/langchain/0.1.0) |
+| farm-haystack | 1.23.0 |
[PyPI](https://pypi.org/project/farm-haystack/1.23.0) |
+| nltk | 3.8.1 | [PyPI](https://pypi.org/project/nltk/3.8.1) |
+| langdetect | 1.0.9 | [PyPI](https://pypi.org/project/langdetect/1.0.9) |
+| plotly | 5.19.0 | [PyPI](https://pypi.org/project/plotly/5.19.0) |
+| ollama | 0.1.9 | [PyPI](https://pypi.org/project/ollama/0.1.9) |
+| joblib | 1.4.2 | [PyPI](https://pypi.org/project/joblib/1.4.2) |
+| lightgbm | 4.5.0 | [PyPI](https://pypi.org/project/lightgbm/4.5.0) |
+| feedparser | 6.0.11 | [PyPI](https://pypi.org/project/feedparser/6.0.11) |
+| numpy | 1.26.4 | [PyPI](https://pypi.org/project/numpy/1.26.4) |
+| scikit-learn | 1.5.2 | [PyPI](https://pypi.org/project/scikit-learn/1.5.2) |
+| bcrypt | 4.2.1 | [PyPI](https://pypi.org/project/bcrypt/4.2.1) |
diff --git a/content/en/docs/reference/runtimes/python/python:v3.12/_index.md
b/content/en/docs/reference/runtimes/python/python:v3.12/_index.md
new file mode 100644
index 00000000..b1fd9b71
--- /dev/null
+++ b/content/en/docs/reference/runtimes/python/python:v3.12/_index.md
@@ -0,0 +1,47 @@
+---
+title: python:v3.12
+description:
+weight: 50
+---
+## python:v3.12 (default: false)
+
+| Library | Version | Link |
+|------------|---------|------|
+| beautifulsoup4 | 4.10.0 |
[PyPI](https://pypi.org/project/beautifulsoup4/4.10.0) |
+| ollama | 0.4.5 | [PyPI](https://pypi.org/project/ollama/0.4.5) |
+| openai | 1.59.3 | [PyPI](https://pypi.org/project/openai/1.59.3) |
+| pymilvus | 2.5.3 | [PyPI](https://pypi.org/project/pymilvus/2.5.3) |
+| redis | 5.2.1 | [PyPI](https://pypi.org/project/redis/5.2.1) |
+| pillow | 11.1.0 | [PyPI](https://pypi.org/project/pillow/11.1.0) |
+| nltk | 3.8.1 | [PyPI](https://pypi.org/project/nltk/3.8.1) |
+| httplib2 | 0.19.1 | [PyPI](https://pypi.org/project/httplib2/0.19.1) |
+| kafka_python | 2.0.2 | [PyPI](https://pypi.org/project/kafka_python/2.0.2) |
+| python-dateutil | 2.8.2 |
[PyPI](https://pypi.org/project/python-dateutil/2.8.2) |
+| requests | 2.32.2 | [PyPI](https://pypi.org/project/requests/2.32.2) |
+| scrapy | 2.5.0 | [PyPI](https://pypi.org/project/scrapy/2.5.0) |
+| simplejson | 3.17.5 | [PyPI](https://pypi.org/project/simplejson/3.17.5) |
+| twisted | 21.7.0 | [PyPI](https://pypi.org/project/twisted/21.7.0) |
+| netifaces | 0.11.0 | [PyPI](https://pypi.org/project/netifaces/0.11.0) |
+| pyyaml | 6.0.2 | [PyPI](https://pypi.org/project/pyyaml/6.0.2) |
+| boto3 | 1.35.98 | [PyPI](https://pypi.org/project/boto3/1.35.98) |
+| psycopg | 3.1.10 | [PyPI](https://pypi.org/project/psycopg/3.1.10) |
+| pymongo | 4.4.1 | [PyPI](https://pypi.org/project/pymongo/4.4.1) |
+| minio | 7.1.16 | [PyPI](https://pypi.org/project/minio/7.1.16) |
+| auth0-python | 4.6.0 | [PyPI](https://pypi.org/project/auth0-python/4.6.0) |
+| langdetect | 1.0.9 | [PyPI](https://pypi.org/project/langdetect/1.0.9) |
+| plotly | 5.19.0 | [PyPI](https://pypi.org/project/plotly/5.19.0) |
+| joblib | 1.4.2 | [PyPI](https://pypi.org/project/joblib/1.4.2) |
+| lightgbm | 4.5.0 | [PyPI](https://pypi.org/project/lightgbm/4.5.0) |
+| feedparser | 6.0.11 | [PyPI](https://pypi.org/project/feedparser/6.0.11) |
+| numpy | 1.26.4 | [PyPI](https://pypi.org/project/numpy/1.26.4) |
+| scikit-learn | 1.5.2 | [PyPI](https://pypi.org/project/scikit-learn/1.5.2) |
+| langchain | 0.3.14 | [PyPI](https://pypi.org/project/langchain/0.3.14) |
+| langchain-ollama | 0.2.2 |
[PyPI](https://pypi.org/project/langchain-ollama/0.2.2) |
+| langchain-openai | 0.2.14 |
[PyPI](https://pypi.org/project/langchain-openai/0.2.14) |
+| langchain-anthropic | 0.3.1 |
[PyPI](https://pypi.org/project/langchain-anthropic/0.3.1) |
+| langchain-together | 0.2.0 |
[PyPI](https://pypi.org/project/langchain-together/0.2.0) |
+| langchain-postgres | 0.0.12 |
[PyPI](https://pypi.org/project/langchain-postgres/0.0.12) |
+| langchain-milvus | 0.1.7 |
[PyPI](https://pypi.org/project/langchain-milvus/0.1.7) |
+| bcrypt | 4.2.1 | [PyPI](https://pypi.org/project/bcrypt/4.2.1) |
+| chevron | 0.14.0 | [PyPI](https://pypi.org/project/chevron/0.14.0) |
+| chess | 1.11.1 | [PyPI](https://pypi.org/project/chess/1.11.1) |
diff --git a/tasks/runtimes/.gitignore b/tasks/runtimes/.gitignore
new file mode 100644
index 00000000..d4b3ea6b
--- /dev/null
+++ b/tasks/runtimes/.gitignore
@@ -0,0 +1,37 @@
+# dependencies (bun install)
+node_modules
+
+# output
+out
+dist
+*.tgz
+
+# code coverage
+coverage
+*.lcov
+
+# logs
+logs
+_.log
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# caches
+.eslintcache
+.cache
+*.tsbuildinfo
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
+
+#lock
+.lock
\ No newline at end of file
diff --git a/tasks/runtimes/README.md b/tasks/runtimes/README.md
new file mode 100644
index 00000000..1a795ec3
--- /dev/null
+++ b/tasks/runtimes/README.md
@@ -0,0 +1,15 @@
+# runtimes
+
+To install dependencies:
+
+```bash
+bun install
+```
+
+To run:
+
+```bash
+bun run index.ts
+```
+
+This project was created using `bun init` in bun v1.2.5. [Bun](https://bun.sh)
is a fast all-in-one JavaScript runtime.
diff --git a/tasks/runtimes/package.json b/tasks/runtimes/package.json
new file mode 100644
index 00000000..aa5fa77e
--- /dev/null
+++ b/tasks/runtimes/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "runtimes",
+ "module": "index.ts",
+ "type": "module",
+ "private": true,
+ "scripts": {
+ "test": "bun test",
+ "test-coverage": "bun test --coverage",
+ "build": "bun build --target=bun src/index.ts --outdir=../"
+ },
+ "devDependencies": {
+ "@types/bun": "latest"
+ },
+ "peerDependencies": {
+ "typescript": "^5"
+ }
+}
\ No newline at end of file
diff --git a/tasks/runtimes/src/index.ts b/tasks/runtimes/src/index.ts
new file mode 100644
index 00000000..410cdebb
--- /dev/null
+++ b/tasks/runtimes/src/index.ts
@@ -0,0 +1,77 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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
+//
+// http://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 { getKindName, getRuntimes, getRuntimeHeaderMD } from './lib/utils'
+import { handleRuntimeKind } from './lib/runtimes'
+import { normalize, join} from 'path'
+import { mkdir, writeFile, } from 'fs/promises'
+
+const validRuntimes = ['nodejs', 'php', 'python']
+
+
+const main = async () => {
+ try {
+ const runtimes = await getRuntimes()
+ if(!runtimes) {
+ console.error('No runtimes found')
+ return
+ }
+ const promises: any[] = []
+ for(const runtimeName in runtimes?.runtimes) {
+ if(validRuntimes.includes(runtimeName)) {
+ const kinds = runtimes?.runtimes[runtimeName]
+ const docRuntimesPath =
normalize('content/en/docs/reference/runtimes')
+ const runtimePath = join(docRuntimesPath,runtimeName)
+ //remove invalid kinds
+ const validKinds = kinds!.filter((kind) => {
+ const {image} = kind
+ return image.prefix === "apache" && image.tag?.trim() !==
''
+ })
+ if(validKinds.length > 0 ) {
+ for(const validKind of validKinds) {
+ const kindName = getKindName(runtimeName,
validKind.image.tag)
+ const kindPath = join(runtimePath, kindName)
+ await mkdir(kindPath, {recursive: true})
+
promises.push(handleRuntimeKind(validKind,kindName,runtimeName,kindPath))
+ }
+ validKinds.forEach(async (kind) => {
+ })
+ // Removed. It's override language guides
+ // promises.push(
+ // writeFile(
+ // join(runtimePath, '_index.md'),
+ // getRuntimeHeaderMD(runtimeName)
+ // )
+ // )
+ }
+ } else {
+ console.log(`Skip ${runtimeName}`)
+ }
+ }
+ await Promise.all(promises)
+ } catch (error) {
+ console.error(error)
+ return 1
+ }
+ return 0
+}
+
+
+// *** Main ***
+main().then((res) => {
+ process.exit(res);
+});
\ No newline at end of file
diff --git a/tasks/runtimes/src/lib/runtimes.ts
b/tasks/runtimes/src/lib/runtimes.ts
new file mode 100644
index 00000000..3fe280ad
--- /dev/null
+++ b/tasks/runtimes/src/lib/runtimes.ts
@@ -0,0 +1,122 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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
+//
+// http://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 type { kind } from "./types"
+import {
+ getKindHeaderMD, handlePHPExtension,
+ getDependecyRowMD, getTableHeader, getDependecyFile,
+ } from './utils'
+import { join } from 'path'
+import { writeFile } from 'fs/promises'
+
+const getNodeJSDependecies = async (runtime: string, kind: string) => {
+ let mdFile = getTableHeader()
+
+ //TODO define type for package.JSON
+ const {dependencies} = JSON.parse(await getDependecyFile(runtime, kind,
'package.json'))
+ for(const dependecy in dependencies) {
+ mdFile += getDependecyRowMD({
+ name: dependecy,
+ version: dependencies[dependecy],
+ docName: 'npm',
+ docUrl:
`https://npmjs.com/package/v/${dependencies[dependecy].replace('^','')}`
+ })
+ }
+ return mdFile
+}
+
+const getPHPDependecies = async (runtime: string, kind: string) => {
+ let mdFile = getTableHeader()
+ const {require: dependencies} = JSON.parse(await getDependecyFile(runtime,
kind, 'composer.json'))
+
+ for(const dependecy in dependencies) {
+ const version:string = dependencies[dependecy]
+ mdFile += getDependecyRowMD({
+ name: dependecy,
+ version: version.replaceAll(/\|/g, '\\|'),
+ docName: 'packagist',
+ docUrl:
`https://packagist.org/packages/${dependecy}#${version.split('|')[0]}`
+ })
+ }
+
+ const dockerDependencies = await getDependecyFile(runtime, kind,
'Dockerfile')
+ const match =
dockerDependencies.match(/install-php-extensions\s+\\\n((?:\s+\w+\s+\\\n)+)/)
+
+ if (match) {
+ match[1]!
+ .split('\\\n')
+ .filter((extension:string) => extension.length > 0)
+ .forEach((extension:string) => {
+ const trimExt = extension.trim()
+ mdFile += getDependecyRowMD({
+ name: trimExt,
+ version: 'Latest*',
+ docName: 'PHP.net',
+ docUrl: handlePHPExtension(trimExt)
+ })
+ })
+ }
+ mdFile += '\n\n *Latest version available for current runtime version'
+ return mdFile
+}
+
+const getPythonDependecies = async(runtime: string, kind: string) => {
+ let mdFile = getTableHeader()
+
+ const dependencies = (await getDependecyFile(runtime, kind,
'requirements.txt')).split('\n')
+ for(const dependecy of dependencies) {
+ const dependecyParts = dependecy.split('==')
+ if(dependecyParts.length === 2) {
+ const [name, version] = dependecyParts
+ mdFile += getDependecyRowMD({
+ name: name || '',
+ version: version || '',
+ docName: 'PyPI',
+ docUrl: `https://pypi.org/project/${name}/${version}`
+ })
+ }
+ }
+ return mdFile
+}
+
+export const getRuntimeDependecies = async (runtime: string, kind: string) => {
+ let result
+ switch(runtime) {
+ case 'nodejs':
+ result = await getNodeJSDependecies(runtime, kind)
+ break
+ case 'python':
+ result = await getPythonDependecies(runtime, kind)
+ break
+ case 'php':
+ result = await getPHPDependecies(runtime, kind)
+ break
+ default:
+ console.log(`invalid runtime ${runtime}`)
+ result = ''
+ }
+ return result
+}
+
+export const handleRuntimeKind = async (kind: kind, kindName: string,
runtimeName: string, kindPath: string) => {
+ const { image, default: kindDefault } = kind;
+ const kindIndexPath = join(kindPath, '_index.md')
+ const dependecies = await getRuntimeDependecies(runtimeName,
image.tag.split('-')[0]!)
+ let kindMD = getKindHeaderMD(kindName, kindDefault)
+ kindMD += `\n\n${dependecies}`
+ await writeFile(kindIndexPath, kindMD)
+}
\ No newline at end of file
diff --git a/tasks/runtimes/src/lib/types.ts b/tasks/runtimes/src/lib/types.ts
new file mode 100644
index 00000000..df97cf63
--- /dev/null
+++ b/tasks/runtimes/src/lib/types.ts
@@ -0,0 +1,82 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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
+//
+// http://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.
+
+export type OpsRuntimes = {
+ description: string[];
+ runtimes: Runtimes;
+ blackboxes: Blackbox[];
+}
+
+export type Blackbox = {
+ prefix: Prefix;
+ name: string;
+ tag: string;
+}
+
+export enum Prefix {
+ Apache = "apache",
+ GhcrIoNuvolaris = "ghcr.io/nuvolaris",
+ Openwhisk = "openwhisk",
+}
+
+export type Runtimes = {
+ [key: string]: kind[];
+}
+
+export type kind = {
+ kind: string;
+ default: boolean;
+ deprecated: boolean;
+ requireMain?: boolean;
+ image: Blackbox;
+ attached?: Attached;
+ stemCells?: StemCell[];
+}
+
+export type Attached = {
+ attachmentName: AttachmentName;
+ attachmentType: AttachmentType;
+}
+
+export enum AttachmentName {
+ Codefile = "codefile",
+}
+
+export enum AttachmentType {
+ TextPlain = "text/plain",
+}
+
+export type StemCell = {
+ initialCount: number;
+ memory: string;
+ reactive: Reactive;
+}
+
+export type Reactive = {
+ minCount: number;
+ maxCount: number;
+ ttl: string;
+ threshold: number;
+ increment: number;
+}
+
+export type Dependecy = {
+ name: string;
+ version: string;
+ docName: string;
+ docUrl: string;
+}
diff --git a/tasks/runtimes/src/lib/utils.ts b/tasks/runtimes/src/lib/utils.ts
new file mode 100644
index 00000000..308eaa5d
--- /dev/null
+++ b/tasks/runtimes/src/lib/utils.ts
@@ -0,0 +1,76 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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
+//
+// http://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 { readFile } from 'fs/promises'
+import type { OpsRuntimes, Dependecy } from './types'
+import { join } from 'path'
+
+export const getRuntimes = async () => {
+ const filePath = 'openserverless-task/runtimes.json'
+ try {
+ const data = await readFile(filePath, 'utf-8')
+ const result:OpsRuntimes = JSON.parse(data)
+ return result
+ } catch (error) {
+ console.error('Error reading runtimes.json:', error)
+ }
+}
+
+export const getKindHeaderMD = (name: string, isDefault: Boolean) => {
+ return `---\ntitle: ${name}\ndescription: \nweight: 50\n---\n## ${name}
(default: ${isDefault})`
+}
+
+export const getRuntimeHeaderMD = (name: string) => {
+ //expand phjs regex to include more runtimes
+ const formattedName = name.replace(/\b\w{1}|[phjs]{2,}/gi, match =>
match.toUpperCase())
+ return `---\ntitle: ${formattedName}\ndescription: \nweight: 50\n---\n`
+}
+
+export const getTableHeader = () => {
+ let header = '| Library | Version | Link |\n'
+ header += '|------------|---------|------|\n'
+ return header
+}
+
+export const getDependecyRowMD = (dependecy: Dependecy) => {
+ const {name, version, docName, docUrl} = dependecy
+ return `| ${name} | ${version} | [${docName}](${docUrl}) |\n`
+}
+
+export const getKindName = (runtime: string, tag: string) => {
+ return `${runtime}:${tag.split('-')[0]}`
+}
+
+
+export const getDependecyFile = async (runtime: string, kind: string,
dependecyFileName: string) => {
+ const dependecyPath = join('openserverless-runtimes','runtime', runtime,
kind, dependecyFileName)
+ return await readFile(dependecyPath, 'utf-8')
+}
+
+export const handlePHPExtension = (extension: string) => {
+ let url = '';
+ if (extension === "gd") {
+ url = "https://www.php.net/manual/en/book.image.php";
+ } else if (/^pdo_/.test(extension)) {
+ url =
`https://www.php.net/manual/en/ref.pdo-${extension.slice(4).toLowerCase()}.php`;
+ } else if (/^(mongodb|redis)$/.test(extension)) {
+ url = `https://pecl.php.net/package/${extension}`;
+ } else {
+ url =
`https://www.php.net/manual/en/book.${extension.toLowerCase()}.php`;
+ }
+ return url;
+}
diff --git a/tasks/runtimes/src/tests/runtimes.test.ts
b/tasks/runtimes/src/tests/runtimes.test.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/tasks/runtimes/src/tests/utils.test.ts
b/tasks/runtimes/src/tests/utils.test.ts
new file mode 100644
index 00000000..ac9adffb
--- /dev/null
+++ b/tasks/runtimes/src/tests/utils.test.ts
@@ -0,0 +1,140 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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
+//
+// http://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 { describe, expect, it, jest, mock} from "bun:test";
+import {
+ getRuntimes, getKindHeaderMD, getRuntimeHeaderMD,
+ getTableHeader, getDependecyRowMD, getKindName,
+ getDependecyFile, handlePHPExtension,
+} from '../lib/utils';
+import type {OpsRuntimes, Dependecy } from '../lib/types'
+import { readFile } from 'fs/promises';
+import { join } from 'path';
+import { Prefix, AttachmentName, AttachmentType } from "../lib/types";
+
+
+describe('Utils', () => {
+ const mockRuntimes: OpsRuntimes = {
+ description: ["Test runtimes"],
+ runtimes: {
+ nodejs: [{
+ kind: "nodejs:18",
+ default: true,
+ deprecated: false,
+ image: { prefix: Prefix.Apache, name: "nodejs", tag: "18" },
+ attached: { attachmentName: AttachmentName.Codefile,
attachmentType: AttachmentType.TextPlain }
+ }]
+ },
+ blackboxes: []
+ };
+
+ describe('getRuntimes', () => {
+ it('should return parsed runtimes data', async () => {
+ const mockJson = JSON.stringify(mockRuntimes);
+ mock.module('fs/promises', () => ({
+ readFile: () => Promise.resolve(mockJson)
+ }));
+ const result = await getRuntimes();
+ expect(result).toEqual(mockRuntimes);
+ });
+ });
+
+ describe('getKindHeaderMD', () => {
+ it('should generate correct markdown header with default', () => {
+ const result = getKindHeaderMD('NodeJS', true);
+ expect(result).toContain('## NodeJS (default: true)');
+ });
+
+ it('should generate header without default', () => {
+ const result = getKindHeaderMD('Python', false);
+ expect(result).toContain('## Python (default: false)');
+ });
+ });
+
+ describe('getRuntimeHeaderMD', () => {
+ it('should format runtime name correctly', () => {
+ const result = getRuntimeHeaderMD('phjs');
+ expect(result).toContain('title: Phjs');
+ });
+
+ it('should handle uppercase conversion', () => {
+ const result = getRuntimeHeaderMD('python3');
+ expect(result).toContain('title: Python3');
+ });
+ });
+
+ describe('getTableHeader', () => {
+ it('should return correct markdown table headers', () => {
+ const result = getTableHeader();
+ expect(result).toBe('| Library | Version | Link
|\n|------------|---------|------|\n');
+ });
+ });
+
+ describe('getDependecyRowMD', () => {
+ const mockDependency: Dependecy = {
+ name: 'express',
+ version: '4.18.2',
+ docName: 'Express Docs',
+ docUrl: 'https://expressjs.com/'
+ };
+
+ it('should generate correct markdown row', () => {
+ const result = getDependecyRowMD(mockDependency);
+ expect(result).toBe('| express | 4.18.2 | [Express
Docs](https://expressjs.com/) |\n');
+ });
+ });
+
+ describe('getKindName', () => {
+ it('should extract version from tag', () => {
+ const result = getKindName('nodejs', '18-alpine');
+ expect(result).toBe('nodejs:18');
+ });
+
+ it('should handle tags without hyphen', () => {
+ const result = getKindName('python', '3.11');
+ expect(result).toBe('python:3.11');
+ });
+ });
+
+ describe('getDependecyFile', () => {
+ it('should read correct dependency file', async () => {
+ (join as jest.Mock).mockReturnValue('mocked/path');
+ (readFile as jest.Mock).mockResolvedValue('{}');
+
+ await getDependecyFile('nodejs', '18', 'dependencies.json');
+ expect(join).toHaveBeenCalledWith('openserverless-runtimes',
'runtime', 'nodejs', '18', 'dependencies.json');
+ });
+ });
+
+ describe('handlePHPExtension', () => {
+ it('should return image URL for gd', () => {
+
expect(handlePHPExtension('gd')).toBe('https://www.php.net/manual/en/book.image.php');
+ });
+
+ it('should handle PDO extensions', () => {
+
expect(handlePHPExtension('pdo_mysql')).toBe('https://www.php.net/manual/en/ref.pdo-mysql.php');
+ });
+
+ it('should handle pecl extensions', () => {
+
expect(handlePHPExtension('mongodb')).toBe('https://pecl.php.net/package/mongodb');
+ });
+
+ it('should return default documentation URL', () => {
+
expect(handlePHPExtension('curl')).toBe('https://www.php.net/manual/en/book.curl.php');
+ });
+ });
+});
\ No newline at end of file
diff --git a/tasks/runtimes/tsconfig.json b/tasks/runtimes/tsconfig.json
new file mode 100644
index 00000000..ab0f0b09
--- /dev/null
+++ b/tasks/runtimes/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ // Environment setup & latest features
+ "lib": ["esnext"],
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ // Bundler mode
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ // Best practices
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+
+ // Some stricter flags (disabled by default)
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ }
+}