├── .changeset ├── README.md └── config.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── plan.md ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── build.yml │ ├── prerelease.yml.disabled │ ├── test-cli.yml │ └── version-packages.yml.disabled ├── .gitignore ├── .husky └── pre-commit ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── biome.json ├── core ├── runner │ ├── CHANGELOG.md │ ├── buf.gen.yaml │ ├── buf.yaml │ ├── nodemon.json │ ├── package.json │ ├── proto │ │ └── node.proto │ ├── src │ │ ├── Configuration.ts │ │ ├── ConfigurationResolver.ts │ │ ├── DefaultLogger.ts │ │ ├── LocalStorage.ts │ │ ├── MemoryUsage.ts │ │ ├── NanoService.ts │ │ ├── NanoServiceResponse.ts │ │ ├── NodeGrpcClient.ts │ │ ├── NodeGrpcNativeClient.ts │ │ ├── NodeMap.ts │ │ ├── NodeRuntime.ts │ │ ├── ResolverBase.ts │ │ ├── Runner.ts │ │ ├── RunnerNode.ts │ │ ├── RunnerNodeBase.ts │ │ ├── RunnerSteps.ts │ │ ├── TriggerBase.ts │ │ ├── gen │ │ │ └── node_pb.ts │ │ ├── index.ts │ │ └── types │ │ │ ├── Average.ts │ │ │ ├── Condition.ts │ │ │ ├── Conditions.ts │ │ │ ├── Config.ts │ │ │ ├── Flow.ts │ │ │ ├── GlobalOptions.ts │ │ │ ├── Inputs.ts │ │ │ ├── JsonLikeObject.ts │ │ │ ├── Mapper.ts │ │ │ ├── Node.ts │ │ │ ├── ParamsDictionary.ts │ │ │ ├── Properties.ts │ │ │ ├── Targets.ts │ │ │ ├── Trigger.ts │ │ │ ├── TriggerHttp.ts │ │ │ ├── TriggerResponse.ts │ │ │ ├── Triggers.ts │ │ │ └── TryCatch.ts │ ├── test │ │ ├── localstorage.test.ts │ │ ├── nanoservice-base.test.ts │ │ └── step-condition.test.ts │ └── tsconfig.json ├── shared │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── nodemon.json │ ├── package.json │ ├── src │ │ ├── GlobalError.ts │ │ ├── GlobalLogger.ts │ │ ├── Metrics.ts │ │ ├── NodeBase.ts │ │ ├── Trigger.ts │ │ ├── index.ts │ │ ├── types │ │ │ ├── ConfigContext.ts │ │ │ ├── Context.ts │ │ │ ├── EnvContext.ts │ │ │ ├── ErrorContext.ts │ │ │ ├── FunctionContext.ts │ │ │ ├── LoggerContext.ts │ │ │ ├── NodeConfigContext.ts │ │ │ ├── ParamsDictionary.ts │ │ │ ├── RequestContext.ts │ │ │ ├── ResponseContext.ts │ │ │ ├── Step.ts │ │ │ └── VarsContext.ts │ │ └── utils │ │ │ ├── CpuUsage.ts │ │ │ ├── Mapper.ts │ │ │ ├── MemoryUsage.ts │ │ │ ├── MetricsBase.ts │ │ │ ├── Time.ts │ │ │ └── index.ts │ └── tsconfig.json └── workflow-helper │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ ├── components │ │ ├── AddElse.ts │ │ ├── AddIf.ts │ │ ├── HelperResponse.ts │ │ ├── Response.ts │ │ ├── StepNode.ts │ │ ├── Trigger.ts │ │ └── Workflow.ts │ ├── index.ts │ └── types │ │ ├── StepOpts.ts │ │ ├── TriggerOpts.ts │ │ └── WorkflowOpts.ts │ ├── tests │ ├── condition.test.ts │ ├── step.test.ts │ └── workflow.test.ts │ └── tsconfig.json ├── dockerfiles ├── Dockerfile ├── Dockerfile.deploy.http └── Dockerfile.node ├── docs ├── assets │ ├── .DS_Store │ ├── favicon.svg │ ├── images │ │ ├── .DS_Store │ │ ├── ai-database-query.jpeg │ │ ├── nanoservices-dark.png │ │ └── nanoservices-light.png │ └── logo │ │ ├── .DS_Store │ │ ├── dark.svg │ │ └── light.svg ├── c │ ├── core │ │ ├── runner.mdx │ │ └── shared.mdx │ ├── devtools │ │ └── index.mdx │ ├── extensions │ │ ├── cli.mdx │ │ ├── nodes.mdx │ │ └── triggers.mdx │ ├── migration-guides │ │ └── index.mdx │ └── overview │ │ ├── architecture.mdx │ │ └── how-to-contribute.mdx ├── d │ ├── cli │ │ ├── build.mdx │ │ ├── deploy.mdx │ │ ├── generate-ai-node.mdx │ │ ├── login.mdx │ │ ├── monitor.mdx │ │ ├── nodes.mdx │ │ ├── overview.mdx │ │ ├── project.mdx │ │ └── workflows.mdx │ ├── deployment │ │ └── index.mdx │ ├── examples │ │ ├── countries.mdx │ │ ├── db-manager.mdx │ │ └── index.mdx │ ├── fundamentals │ │ ├── architecture-comparison.mdx │ │ └── nanoservices-concept.mdx │ └── introduction │ │ ├── context.mdx │ │ ├── first-steps.mdx │ │ ├── nodes.mdx │ │ ├── overview.mdx │ │ ├── triggers.mdx │ │ └── workflows.mdx ├── docs.json ├── generateRefLinks.js ├── o │ ├── resources │ │ └── index.mdx │ └── support │ │ └── index.mdx └── ref │ ├── .nojekyll │ ├── README.md │ ├── classes │ ├── runner_src.Configuration.md │ ├── runner_src.ConfigurationResolver.md │ ├── runner_src.DefaultLogger.md │ ├── runner_src.LocalStorage.md │ ├── runner_src.NanoService.md │ ├── runner_src.NanoServiceResponse.md │ ├── runner_src.NodeMap.md │ ├── runner_src.ResolverBase.md │ ├── runner_src.Runner.md │ ├── runner_src.RunnerSteps.md │ ├── runner_src.TriggerBase.md │ ├── shared_src.GlobalError.md │ ├── shared_src.GlobalLogger.md │ ├── shared_src.MemoryUsage.md │ ├── shared_src.Metrics.md │ ├── shared_src.NodeBase.md │ └── shared_src.Trigger.md │ ├── interfaces │ ├── runner_src.INanoServiceResponse.md │ ├── runner_src.JsonLikeObject.md │ └── runner_src.ParamsDictionary.md │ └── modules │ ├── runner_src.md │ └── shared_src.md ├── infra ├── development │ ├── 3055.dat │ ├── 3057.dat │ ├── 3059.dat │ ├── 3061.dat │ ├── 3062.dat │ ├── 3063.dat │ ├── 3065.dat │ ├── 3067.dat │ ├── 3069.dat │ ├── 3071.dat │ ├── 3073.dat │ ├── 3075.dat │ ├── 3077.dat │ ├── 3079.dat │ ├── 3081.dat │ ├── docker-compose.yml │ ├── schema.sql │ └── toc.dat ├── metrics │ ├── dashboard.json │ ├── datasources.yml │ ├── default.yaml │ ├── docker-compose.yml │ ├── loki-config.yaml │ ├── nginx.conf │ ├── prometheus.yml │ ├── telegraf.conf │ └── tempo.yaml └── milvus │ ├── .gitignore │ └── docker-compose.yml ├── nodes ├── control-flow │ └── if-else@1.0.0 │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── config.json │ │ ├── index.ts │ │ ├── inputSchema.ts │ │ ├── nodemon.json │ │ ├── package.json │ │ ├── test │ │ └── index.test.ts │ │ └── tsconfig.json └── web │ ├── .DS_Store │ ├── api-call@1.0.0 │ ├── CHANGELOG.md │ ├── README.md │ ├── config.json │ ├── index.ts │ ├── inputSchema.ts │ ├── nodemon.json │ ├── package.json │ ├── test │ │ └── index.test.ts │ ├── tsconfig.json │ └── util.ts │ └── react@1.0.0 │ ├── .DS_Store │ ├── CHANGELOG.md │ ├── README.md │ ├── app │ └── index.jsx │ ├── babel.config.json │ ├── config.json │ ├── index.html │ ├── index.ts │ ├── inputSchema.ts │ ├── nodemon.json │ ├── package.json │ ├── test │ ├── helper.ts │ ├── index.mockup.html │ └── index.test.ts │ └── tsconfig.json ├── nx.json ├── package.json ├── packages └── cli │ ├── .gitignore │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ ├── commands │ │ ├── build │ │ │ └── index.ts │ │ ├── create │ │ │ ├── node.ts │ │ │ ├── project.ts │ │ │ ├── utils │ │ │ │ └── Examples.ts │ │ │ └── workflow.ts │ │ ├── deploy │ │ │ └── index.ts │ │ ├── dev │ │ │ └── index.ts │ │ ├── generate │ │ │ ├── NodeFileWriter.ts │ │ │ ├── NodeGenerator.ts │ │ │ ├── RegisterNode.ts │ │ │ ├── index.ts │ │ │ └── prompts │ │ │ │ ├── create-node-manifest.system.ts │ │ │ │ ├── create-node.system.ts │ │ │ │ └── register-node.system.ts │ │ ├── login │ │ │ └── index.ts │ │ ├── logout │ │ │ └── index.ts │ │ └── monitor │ │ │ ├── index.ts │ │ │ ├── monitor-component.tsx │ │ │ ├── static-web-server.ts │ │ │ └── static │ │ │ └── index.html │ ├── index.ts │ └── services │ │ ├── commander.ts │ │ ├── constants.ts │ │ ├── local-token-manager.ts │ │ ├── package-manager.ts │ │ ├── posthog.ts │ │ └── utils.ts │ ├── tests │ └── commands │ │ ├── build │ │ └── build.test.ts │ │ ├── create │ │ ├── node.test.ts │ │ └── project.test.ts │ │ ├── deploy │ │ └── deploy.test.ts │ │ ├── login │ │ └── login.test.ts │ │ └── logout │ │ └── logout.test.ts │ ├── tsconfig.json │ └── tsconfig.jsondisabled ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── runtimes ├── proto │ └── node.proto └── python3 │ ├── Makefile │ ├── __init__.py │ ├── core │ ├── __init__.py │ ├── nanoservice.py │ ├── node_base.py │ ├── types │ │ ├── config.py │ │ ├── context.py │ │ ├── error.py │ │ ├── global_error.py │ │ ├── logger.py │ │ ├── nanoservice_response.py │ │ ├── response.py │ │ └── step.py │ └── util │ │ └── mapper.py │ ├── gen │ ├── __init__.py │ ├── node_pb2.py │ └── node_pb2_grpc.py │ ├── nodemon.json │ ├── nodes │ ├── __init__.py │ ├── api_call │ │ ├── node.py │ │ └── test_index.py │ ├── embed │ │ ├── README.md │ │ ├── __init__.py │ │ ├── node.py │ │ └── test_node.py │ ├── generate_pdf │ │ ├── node.py │ │ └── reports │ │ │ └── .gitkeep │ ├── image_description │ │ └── node.py │ ├── milvus │ │ ├── insert │ │ │ ├── node.py │ │ │ └── test_node.py │ │ └── query │ │ │ └── node.py │ ├── nodes.py │ └── sentiment │ │ └── node.py │ ├── package-lock.json │ ├── package.json │ ├── requirements.txt │ ├── runner.py │ ├── server.py │ ├── tests │ ├── __init__.py │ ├── test_mapper.py │ ├── test_nanoservice.py │ └── test_node_base.py │ └── util │ ├── __init__.py │ └── message_manager.py ├── sdk └── javascript │ ├── README.md │ ├── bootstrap.min.css │ ├── image-ai.html │ ├── nanosdk.js │ ├── nanosdk.min.js │ ├── pdf.html │ └── test.html ├── templates ├── node-ui │ ├── CHANGELOG.md │ ├── README.md │ ├── app │ │ └── index.jsx │ ├── babel.config.json │ ├── config.json │ ├── index.html │ ├── index.ts │ ├── inputSchema.ts │ ├── nodemon.json │ ├── package.json │ ├── test │ │ ├── helper.ts │ │ ├── index.mockup.html │ │ └── index.test.ts │ └── tsconfig.json ├── node │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── config.json │ ├── index.ts │ ├── package.json │ ├── test │ │ ├── helper.ts │ │ └── index.test.ts │ └── tsconfig.json └── ts-template │ ├── nodemon.json │ ├── package.json │ ├── src │ └── index.ts │ └── tsconfig.json ├── triggers ├── .DS_Store ├── grpc │ ├── .env.example │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── buf.gen.yaml │ ├── buf.yaml │ ├── localhost+2-key.pem │ ├── localhost+2.pem │ ├── nodemon.json │ ├── package.json │ ├── proto │ │ └── workflow.proto │ ├── src │ │ ├── GRpcTrigger.ts │ │ ├── GrpcClient.ts │ │ ├── GrpcServer.ts │ │ ├── MessageDecode.ts │ │ ├── NanoSDK.ts │ │ ├── Nodes.ts │ │ ├── Workflows.ts │ │ ├── gen │ │ │ └── workflow_pb.ts │ │ ├── index.ts │ │ ├── opentelemetry_metrics.ts │ │ ├── server.ts │ │ └── types │ │ │ ├── Message.ts │ │ │ ├── RuntimeWorkflow.ts │ │ │ └── Workflows.ts │ ├── tsconfig.json │ └── workflows │ │ └── countries.json └── http │ ├── .DS_Store │ ├── .dockerignore │ ├── .env.example │ ├── .gitignore │ ├── CHANGELOG.md │ ├── Dockerfile │ ├── Dockerfile.dev │ ├── infra │ └── docker-compose.yml │ ├── nodemon.json │ ├── notes │ ├── CLI_Commands │ │ ├── Authentication.md │ │ ├── Building.md │ │ ├── Create_Node.md │ │ ├── Create_Workflow.md │ │ └── Deployment.md │ ├── Core_Concepts │ │ └── Context.md │ ├── examples.md │ └── index.md │ ├── package.json │ ├── public │ ├── favicon.ico │ └── metric │ │ └── index.html │ ├── request.http │ ├── src │ ├── AppRoutes.ts │ ├── Nodes.ts │ ├── Workflows.ts │ ├── index.ts │ ├── nodes │ │ └── examples │ │ │ ├── README.md │ │ │ ├── base64-pdf │ │ │ └── index.ts │ │ │ ├── dashboard-generator │ │ │ ├── ArrayMap.ts │ │ │ ├── DashboardChartsGenerator.ts │ │ │ ├── InMemory.ts │ │ │ ├── MemoryStorage.ts │ │ │ ├── MultipleQueryGeneratorNode.ts │ │ │ └── ui │ │ │ │ ├── index.html │ │ │ │ └── index.ts │ │ │ ├── db-manager │ │ │ ├── MapperNode.ts │ │ │ ├── QueryGeneratorNode.ts │ │ │ └── ui │ │ │ │ ├── index.html │ │ │ │ └── index.ts │ │ │ ├── feedback-ui │ │ │ ├── index.html │ │ │ └── index.ts │ │ │ ├── image-capture │ │ │ ├── index.html │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── mastra-agent │ │ │ ├── README.md │ │ │ ├── index.ts │ │ │ ├── tool.ts │ │ │ ├── ui │ │ │ │ ├── app │ │ │ │ │ ├── chatbot.jsx │ │ │ │ │ └── weather.jsx │ │ │ │ ├── index.html │ │ │ │ ├── index.ts │ │ │ │ └── inputSchema.ts │ │ │ └── util.ts │ │ │ ├── mongodb-query.ts │ │ │ ├── postgres-query │ │ │ ├── README.md │ │ │ └── index.ts │ │ │ ├── save-base64-image │ │ │ └── index.ts │ │ │ └── workflow-docs │ │ │ ├── DirectoryManager.ts │ │ │ ├── ErrorNode.ts │ │ │ ├── FileManager.ts │ │ │ ├── InMemory.ts │ │ │ ├── OpenAI.ts │ │ │ └── ui │ │ │ ├── index.html │ │ │ └── index.ts │ ├── runner │ │ ├── HttpTrigger.ts │ │ ├── MessageDecode.ts │ │ ├── Util.ts │ │ ├── metrics │ │ │ ├── opentelemetry_metrics.ts │ │ │ └── opentelemetry_traces.ts │ │ └── types │ │ │ ├── ErrorResponse.ts │ │ │ ├── NodeTypes.ts │ │ │ ├── Nodes.ts │ │ │ ├── RuntimeWorkflow.ts │ │ │ ├── WorkflowRequest.ts │ │ │ ├── WorkflowResponse.ts │ │ │ └── Workflows.ts │ └── workflows │ │ ├── countries-cats-helper.ts │ │ ├── countries-helper.ts │ │ └── empty.ts │ ├── tsconfig.json │ └── workflows │ ├── json │ ├── countries-py.json │ ├── countries-vs-facts.json │ ├── countries.json │ ├── dashboard-gen.json │ ├── db-manager.json │ ├── empty.json │ ├── feedback.json │ ├── films.json │ ├── image-capture.json │ ├── launches-by-year.json │ ├── loadtest.json │ ├── mongodb.json │ ├── rentals-pdf.json │ └── workflow-docs.json │ ├── toml │ └── countries.toml │ ├── xml │ └── countries.xml │ └── yaml │ └── countries.yaml ├── tsconfig.json └── workflows └── json ├── countries-vs-facts.json ├── countries.json └── weather.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/plan.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Roadmap Item" 3 | about: "Outline a single item or feature in the roadmap." 4 | title: "[Roadmap] Brief Title for the Item" 5 | labels: roadmap 6 | assignees: "" 7 | --- 8 | 9 | ## Summary 10 | _A brief description of the feature or goal._ 11 | 12 | ## Deliverables 13 | - [ ] **What will be delivered?** (e.g., feature, documentation, tests) 14 | 15 | ## Acceptance Criteria 16 | - [ ] Clear criteria to mark this item as complete. 17 | 18 | ## Notes 19 | _Any additional context or links (e.g., designs, discussions, issues)._ 20 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Basic `dependabot.yml` file with 2 | # minimum configuration for three package managers 3 | 4 | version: 2 5 | updates: 6 | # Enable version updates for npm 7 | - package-ecosystem: "npm" 8 | # Look for `package.json` and `lock` files in the `root` directory 9 | directory: "/" 10 | # Check the npm registry for updates every day (weekdays) 11 | schedule: 12 | interval: "weekly" 13 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Pull Request Title 2 | _A clear, descriptive title summarizing the changes._ 3 | 4 | --- 5 | 6 | ## Description 7 | _Provide a brief overview of the changes introduced in this pull request. Include context, purpose, and relevant details._ 8 | 9 | --- 10 | 11 | ## Related Issues 12 | _Reference any related issues or discussions (e.g., "Closes #123")._ 13 | 14 | --- 15 | 16 | ## Changes Made 17 | _Outline the specific changes made in this pull request:_ 18 | - **Feature/Improvement**: Brief description of the feature or improvement. 19 | - **Bug Fix**: Brief description of the bug fix (if applicable). 20 | - **Other Changes**: Describe any additional updates or changes. 21 | 22 | --- 23 | 24 | ## Checklist 25 | _Please ensure the following items are completed:_ 26 | 27 | - [ ] The code follows the project's style guidelines. 28 | - [ ] Unit tests have been added/updated where necessary. 29 | - [ ] Documentation has been updated (if applicable). 30 | - [ ] All tests pass locally. 31 | 32 | --- 33 | 34 | ## Screenshots/Media (if applicable) 35 | _Add screenshots, GIFs, or other media to demonstrate the change._ 36 | 37 | --- 38 | 39 | ## Notes for Reviewers 40 | _Any additional context or notes for reviewers?_ 41 | 42 | --- 43 | 44 | Thank you for your contribution! 45 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Project 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - name: Setup Biome 17 | uses: biomejs/setup-biome@v2 18 | with: 19 | version: latest 20 | 21 | - name: Run Biome 22 | run: biome ci . -------------------------------------------------------------------------------- /.github/workflows/test-cli.yml: -------------------------------------------------------------------------------- 1 | name: CLI Tests 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | paths: 8 | - 'cli/**' 9 | - '.github/workflows/test-cli.yml' 10 | push: 11 | branches: 12 | - nanoctl-cicd 13 | paths: 14 | - 'cli/**' 15 | - '.github/workflows/test-cli.yml' 16 | 17 | jobs: 18 | test: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - name: Install pnpm 24 | uses: pnpm/action-setup@v2 25 | with: 26 | version: 9.15.0 27 | 28 | - name: Setup Node.js 29 | uses: actions/setup-node@v4 30 | with: 31 | node-version: '22' 32 | cache: 'pnpm' 33 | 34 | - name: Install dependencies 35 | run: pnpm install 36 | 37 | - name: Build packages 38 | run: pnpm run build:cli 39 | 40 | - name: Run CLI tests 41 | run: pnpm run cli:test 42 | -------------------------------------------------------------------------------- /.github/workflows/version-packages.yml.disabled: -------------------------------------------------------------------------------- 1 | name: Version Packages 2 | 3 | permissions: 4 | contents: write 5 | pull-requests: write 6 | 7 | on: 8 | push: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | version: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout Repository 17 | uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | token: ${{ secrets.GITHUB_TOKEN }} 21 | 22 | - name: Install pnpm 23 | uses: pnpm/action-setup@v2 24 | with: 25 | version: 9.7.0 26 | 27 | - name: Setup Node.js 28 | uses: actions/setup-node@v4 29 | with: 30 | node-version: "20" 31 | cache: "pnpm" 32 | 33 | - name: Install dependencies 34 | run: pnpm install 35 | 36 | - name: Build 37 | run: pnpm build:core && pnpm build:cli 38 | 39 | - name: Create Release Pull Request 40 | uses: changesets/action@v1 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | with: 44 | title: "chore: version packages" 45 | commit: "chore: version packages" 46 | version: pnpm changeset version 47 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | lint-staged 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | prepare: 2 | echo VITE_WORKFLOWS_PATH=${PWD}/triggers/http/workflows > ${PWD}/core/runner/.env.local 3 | echo VITE_NODES_PATH=${PWD}/triggers/http/src/nodes >> ${PWD}/core/runner/.env.local 4 | 5 | cli-dev: 6 | pnpm --filter nanoctl -r run dev -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "vcs": { 4 | "enabled": false, 5 | "clientKind": "git", 6 | "useIgnoreFile": false 7 | }, 8 | "files": { 9 | "ignoreUnknown": false, 10 | "ignore": ["node_modules", "dist"] 11 | }, 12 | "formatter": { 13 | "enabled": true, 14 | "indentStyle": "tab" 15 | }, 16 | "organizeImports": { 17 | "enabled": true 18 | }, 19 | "linter": { 20 | "enabled": true, 21 | "rules": { 22 | "recommended": true 23 | } 24 | }, 25 | "javascript": { 26 | "formatter": { 27 | "quoteStyle": "double", 28 | "lineWidth": 120 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/runner/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | clean: true 3 | plugins: 4 | - local: protoc-gen-es 5 | opt: target=ts 6 | out: src/gen 7 | include_imports: true -------------------------------------------------------------------------------- /core/runner/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | breaking: 3 | use: 4 | - FILE 5 | lint: 6 | use: 7 | - STANDARD 8 | modules: 9 | - path: proto -------------------------------------------------------------------------------- /core/runner/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": ".ts,.js", 4 | "ignore": [], 5 | "exec": "pnpm --filter @nanoservice/helper run build" 6 | } 7 | -------------------------------------------------------------------------------- /core/runner/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nanoservice-ts/runner", 3 | "version": "0.1.26", 4 | "description": "", 5 | "engines": { 6 | "node": ">=18.0.0" 7 | }, 8 | "main": "dist/index.js", 9 | "types": "dist/index.d.ts", 10 | "files": ["dist"], 11 | "author": "Marco A. Castillo Della Sera", 12 | "license": "MIT", 13 | "scripts": { 14 | "build": "rm -rf dist && mkdir dist && copyfiles -u 1 proto/** dist/proto && npx tsc", 15 | "build:dev": "pnpm run build && tsc --watch", 16 | "test:dev": "vitest", 17 | "test": "vitest run", 18 | "typecheck": "tsc --noEmit --incremental", 19 | "build:proto": "npx buf generate ." 20 | }, 21 | "devDependencies": { 22 | "@bufbuild/buf": "^1.50.0", 23 | "@bufbuild/protoc-gen-es": "^2.2.3", 24 | "@types/node": "^22.13.4", 25 | "@types/lodash": "^4.14.196", 26 | "nodemon": "^3.1.9", 27 | "rimraf": "^6.0.1", 28 | "ts-node": "^10.9.2", 29 | "typescript": "^5.7.2", 30 | "vitest": "^3.1.4", 31 | "copyfiles": "^2.4.1" 32 | }, 33 | "dependencies": { 34 | "@bufbuild/protobuf": "^2.2.3", 35 | "@connectrpc/connect": "^2.0.1", 36 | "@connectrpc/connect-node": "^2.0.1", 37 | "zod": "^3.24.1", 38 | "@nanoservice-ts/shared": "workspace:*", 39 | "@nanoservice-ts/helper": "workspace:*", 40 | "@opentelemetry/api": "^1.9.0", 41 | "uuid": "^11.0.3", 42 | "jsonschema": "^1.4.1", 43 | "lodash": "^4.17.21", 44 | "yaml": "^2.7.0", 45 | "fast-xml-parser": "^5.0.8", 46 | "smol-toml": "^1.3.1", 47 | "@grpc/grpc-js": "^1.13.0", 48 | "@grpc/proto-loader": "^0.7.13" 49 | }, 50 | "private": false 51 | } 52 | -------------------------------------------------------------------------------- /core/runner/proto/node.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package nanoservice.workflow.v1; 4 | 5 | message NodeRequest { 6 | string Name = 1; 7 | string Message = 2; 8 | string Encoding = 3; 9 | string Type = 4; 10 | } 11 | 12 | message NodeResponse { 13 | string Message = 1; 14 | string Encoding = 2; 15 | string Type = 3; 16 | } 17 | 18 | enum MessageEncoding { 19 | BASE64 = 0; 20 | STRING = 1; 21 | } 22 | 23 | enum MessageType { 24 | TEXT = 0; 25 | JSON = 1; 26 | XML = 2; 27 | HTML = 3; 28 | BINARY = 4; 29 | } 30 | 31 | service NodeService { 32 | rpc ExecuteNode (NodeRequest) returns (NodeResponse) {} 33 | } -------------------------------------------------------------------------------- /core/runner/src/ConfigurationResolver.ts: -------------------------------------------------------------------------------- 1 | import LocalStorage from "./LocalStorage"; 2 | import type GlobalOptions from "./types/GlobalOptions"; 3 | import type Targets from "./types/Targets"; 4 | 5 | export default class ConfigurationResolver { 6 | private targets: Targets = {}; 7 | private globalOptions: GlobalOptions = {}; 8 | 9 | constructor(opts: GlobalOptions) { 10 | this.targets = { 11 | local: new LocalStorage(), 12 | }; 13 | 14 | this.globalOptions = opts; 15 | } 16 | 17 | async get(target: string, name: string) { 18 | return await this.targets[target].get(name, this.globalOptions.workflows); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/runner/src/NanoServiceResponse.ts: -------------------------------------------------------------------------------- 1 | import type { GlobalError, NodeBase, ResponseContext } from "@nanoservice-ts/shared"; 2 | import type JsonLikeObject from "./types/JsonLikeObject"; 3 | 4 | export interface INanoServiceResponse extends ResponseContext { 5 | steps: NodeBase[]; 6 | } 7 | 8 | export default class NanoServiceResponse implements INanoServiceResponse { 9 | public steps: NodeBase[]; 10 | public data: string | JsonLikeObject | JsonLikeObject[]; 11 | public error: GlobalError | null; 12 | public success?: boolean | undefined; 13 | public contentType?: string | undefined; 14 | 15 | constructor() { 16 | this.steps = []; 17 | this.data = {}; 18 | this.error = null; 19 | this.success = true; 20 | this.contentType = "application/json"; 21 | } 22 | 23 | setError(error: GlobalError): void { 24 | this.error = error; 25 | this.success = false; 26 | this.data = {}; 27 | } 28 | 29 | setSuccess(data: string | JsonLikeObject | JsonLikeObject[]): void { 30 | this.data = data; 31 | this.error = null; 32 | this.success = true; 33 | } 34 | 35 | setSteps(steps: NodeBase[]): void { 36 | this.steps = steps; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/runner/src/NodeGrpcNativeClient.ts: -------------------------------------------------------------------------------- 1 | import type { CallOptions, NodeRequest, NodeResponse } from "./NodeGrpcClient"; 2 | import type ParamsDictionary from "./types/ParamsDictionary"; 3 | 4 | const grpc = require("@grpc/grpc-js"); 5 | const protoLoader = require("@grpc/proto-loader"); 6 | 7 | const packageDefinition = protoLoader.loadSync(`${__dirname}/proto/node.proto`, { 8 | keepCase: true, 9 | longs: String, 10 | enums: String, 11 | defaults: true, 12 | oneofs: true, 13 | }); 14 | const nodeProto = grpc.loadPackageDefinition(packageDefinition); 15 | 16 | type RpcOptions = { 17 | host: string; 18 | port: number; 19 | }; 20 | 21 | export default class NodeGrpcNativeClient { 22 | protected opts: RpcOptions; 23 | 24 | constructor(host = "localhost", port = 50051) { 25 | this.opts = { 26 | host: host, 27 | port: port, 28 | }; 29 | } 30 | 31 | async call(message: NodeRequest, opts?: CallOptions): Promise { 32 | const response = await this.executeNode(message); 33 | return response as unknown as NodeResponse; 34 | } 35 | 36 | executeNode(message: NodeRequest): Promise { 37 | return new Promise((resolve, reject) => { 38 | const client = new nodeProto.nanoservice.workflow.v1.NodeService( 39 | `${this.opts.host}:${this.opts.port}`, 40 | grpc.credentials.createInsecure(), 41 | { 42 | "grpc.keepalive_time_ms": 10000, // Keep connection alive every 10s 43 | "grpc.keepalive_timeout_ms": 5000, 44 | }, 45 | ); 46 | 47 | client.ExecuteNode(message, (error: Error, response: ParamsDictionary) => { 48 | if (error) { 49 | reject(error); 50 | } else { 51 | resolve(response); 52 | } 53 | }); 54 | }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /core/runner/src/NodeMap.ts: -------------------------------------------------------------------------------- 1 | import type { NodeBase } from "@nanoservice-ts/shared"; 2 | 3 | export default class NodeMap { 4 | public nodes: Map = new Map(); 5 | 6 | public addNode(name: string, node: NodeBase): void { 7 | this.nodes.set(name, node); 8 | } 9 | 10 | public getNode(name: string): NodeBase | undefined { 11 | return this.nodes.get(name); 12 | } 13 | 14 | public getNodes(): Map { 15 | return this.nodes; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/runner/src/ResolverBase.ts: -------------------------------------------------------------------------------- 1 | import type { Context, GlobalLogger } from "@nanoservice-ts/shared"; 2 | import DefaultLogger from "./DefaultLogger"; 3 | import type Config from "./types/Config"; 4 | import type { WorkflowLocator } from "./types/GlobalOptions"; 5 | 6 | abstract class ResolverBase { 7 | abstract get(name: string, workflow: WorkflowLocator): Promise; 8 | 9 | createContext(logger?: GlobalLogger): Context { 10 | const ctx: Context = { 11 | id: "", 12 | config: {}, 13 | request: { body: {} }, 14 | response: { data: "", contentType: "", success: true, error: null }, 15 | error: { message: [] }, 16 | logger: logger || new DefaultLogger(), 17 | eventLogger: null, 18 | _PRIVATE_: null, 19 | }; 20 | 21 | return ctx; 22 | } 23 | } 24 | 25 | export default ResolverBase; 26 | -------------------------------------------------------------------------------- /core/runner/src/Runner.ts: -------------------------------------------------------------------------------- 1 | import type { Context, NodeBase } from "@nanoservice-ts/shared"; 2 | import RunnerSteps from "./RunnerSteps"; 3 | 4 | /** 5 | * Runner class that extends RunnerSteps to execute a series of NanoService steps. 6 | */ 7 | export default class Runner extends RunnerSteps { 8 | private steps: NodeBase[]; 9 | 10 | /** 11 | * Constructs a new Runner instance. 12 | * 13 | * @param steps - An array of NanoService steps to be executed. 14 | */ 15 | constructor(steps: NodeBase[] = []) { 16 | super(); 17 | this.steps = steps; 18 | } 19 | 20 | /** 21 | * Executes the series of NanoService steps with the given context. 22 | * 23 | * @param ctx - The context to be passed through the steps. 24 | * @returns A promise that resolves to the final context after all steps have been executed. 25 | */ 26 | async run(ctx: Context): Promise { 27 | return await this.runSteps(ctx, this.steps); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/runner/src/RunnerNode.ts: -------------------------------------------------------------------------------- 1 | import { type Context, NodeBase, type ResponseContext, type Step } from "@nanoservice-ts/shared"; 2 | 3 | export default abstract class RunnerNode extends NodeBase implements Step { 4 | public node = ""; 5 | public type = ""; 6 | 7 | abstract run(ctx: Context): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /core/runner/src/RunnerNodeBase.ts: -------------------------------------------------------------------------------- 1 | import type Node from "./types/Node"; 2 | 3 | export default abstract class RunnerNodeBase implements Node { 4 | [key: string]: 5 | | import(".").Flow 6 | | import(".").Properties 7 | | import(".").Conditions 8 | | import(".").Condition 9 | | import("./types/Mapper").default 10 | | import("./types/TryCatch").default; 11 | } 12 | -------------------------------------------------------------------------------- /core/runner/src/index.ts: -------------------------------------------------------------------------------- 1 | import Configuration from "./Configuration"; 2 | import ConfigurationResolver from "./ConfigurationResolver"; 3 | import DefaultLogger from "./DefaultLogger"; 4 | import LocalStorage from "./LocalStorage"; 5 | import ResolverBase from "./ResolverBase"; 6 | import Runner from "./Runner"; 7 | import TriggerBase from "./TriggerBase"; 8 | 9 | // types 10 | 11 | import NanoService from "./NanoService"; 12 | import NanoServiceResponse, { INanoServiceResponse } from "./NanoServiceResponse"; 13 | import NodeMap from "./NodeMap"; 14 | import RunnerSteps from "./RunnerSteps"; 15 | import Average from "./types/Average"; 16 | import Condition from "./types/Condition"; 17 | import Conditions from "./types/Conditions"; 18 | import Config from "./types/Config"; 19 | import Flow from "./types/Flow"; 20 | import GlobalOptions from "./types/GlobalOptions"; 21 | import Inputs from "./types/Inputs"; 22 | import JsonLikeObject from "./types/JsonLikeObject"; 23 | import Node from "./types/Node"; 24 | import ParamsDictionary from "./types/ParamsDictionary"; 25 | import Properties from "./types/Properties"; 26 | import Targets from "./types/Targets"; 27 | import Trigger from "./types/Trigger"; 28 | import TriggerHttp from "./types/TriggerHttp"; 29 | import TriggerResponse from "./types/TriggerResponse"; 30 | import Triggers from "./types/Triggers"; 31 | 32 | export { 33 | Configuration, 34 | Runner, 35 | ConfigurationResolver, 36 | DefaultLogger, 37 | LocalStorage, 38 | ResolverBase, 39 | TriggerBase, 40 | Condition, 41 | Conditions, 42 | Config, 43 | Flow, 44 | Inputs, 45 | Node, 46 | Properties, 47 | Targets, 48 | Trigger, 49 | TriggerHttp, 50 | Triggers, 51 | ParamsDictionary, 52 | GlobalOptions, 53 | NodeMap, 54 | JsonLikeObject, 55 | NanoService, 56 | NanoServiceResponse, 57 | INanoServiceResponse, 58 | RunnerSteps, 59 | Average, 60 | TriggerResponse, 61 | }; 62 | -------------------------------------------------------------------------------- /core/runner/src/types/Average.ts: -------------------------------------------------------------------------------- 1 | type Average = { 2 | total: number; 3 | min: number; 4 | max: number; 5 | global_memory: number; 6 | global_free_memory: number; 7 | cpu_percentage: number; 8 | cpu_total: number; 9 | cpu_usage: number; 10 | }; 11 | 12 | export default Average; 13 | -------------------------------------------------------------------------------- /core/runner/src/types/Condition.ts: -------------------------------------------------------------------------------- 1 | import type { NodeBase } from "@nanoservice-ts/shared"; 2 | 3 | type Condition = { 4 | type?: string; 5 | condition: string; 6 | steps?: NodeBase[]; 7 | error?: string; 8 | }; 9 | 10 | export default Condition; 11 | -------------------------------------------------------------------------------- /core/runner/src/types/Conditions.ts: -------------------------------------------------------------------------------- 1 | import type Condition from "./Condition"; 2 | 3 | type Conditions = { 4 | conditions: Condition[]; 5 | }; 6 | 7 | export default Conditions; 8 | -------------------------------------------------------------------------------- /core/runner/src/types/Config.ts: -------------------------------------------------------------------------------- 1 | import type { NodeBase } from "@nanoservice-ts/shared"; 2 | import type RunnerNode from "../RunnerNode"; 3 | import type Node from "./Node"; 4 | import type Trigger from "./Trigger"; 5 | 6 | type Config = { 7 | name: string; 8 | version: string; 9 | steps: NodeBase[] | RunnerNode[]; 10 | nodes: Node; 11 | trigger: Trigger; 12 | }; 13 | 14 | export default Config; 15 | -------------------------------------------------------------------------------- /core/runner/src/types/Flow.ts: -------------------------------------------------------------------------------- 1 | import type RunnerNode from "../RunnerNode"; 2 | 3 | type Flow = { 4 | steps: RunnerNode[]; 5 | }; 6 | 7 | export default Flow; 8 | -------------------------------------------------------------------------------- /core/runner/src/types/GlobalOptions.ts: -------------------------------------------------------------------------------- 1 | import type { HelperResponse } from "@nanoservice-ts/helper"; 2 | import type NodeMap from "../NodeMap"; 3 | 4 | type GlobalOptions = { 5 | nodes: NodeMap; 6 | workflows: WorkflowLocator; 7 | }; 8 | 9 | type WorkflowLocator = { [key: string]: HelperResponse }; 10 | 11 | export default GlobalOptions; 12 | export type { WorkflowLocator }; 13 | -------------------------------------------------------------------------------- /core/runner/src/types/Inputs.ts: -------------------------------------------------------------------------------- 1 | import type Properties from "./Properties"; 2 | 3 | type Inputs = { 4 | inputs: Properties; 5 | }; 6 | 7 | export default Inputs; 8 | -------------------------------------------------------------------------------- /core/runner/src/types/JsonLikeObject.ts: -------------------------------------------------------------------------------- 1 | export default interface JsonLikeObject { 2 | [key: string]: string | number | boolean | JsonLikeObject | JsonLikeObject[]; 3 | } 4 | -------------------------------------------------------------------------------- /core/runner/src/types/Mapper.ts: -------------------------------------------------------------------------------- 1 | import type RunnerNode from "../RunnerNode"; 2 | 3 | type Mapper = { 4 | mapper: RunnerNode; 5 | }; 6 | 7 | export default Mapper; 8 | -------------------------------------------------------------------------------- /core/runner/src/types/Node.ts: -------------------------------------------------------------------------------- 1 | import type Condition from "./Condition"; 2 | import type Conditions from "./Conditions"; 3 | import type Flow from "./Flow"; 4 | import type Mapper from "./Mapper"; 5 | import type Properties from "./Properties"; 6 | import type TryCatch from "./TryCatch"; 7 | 8 | type Node = { 9 | [key: string]: Flow | Properties | Conditions | Condition | Mapper | TryCatch; 10 | }; 11 | 12 | export default Node; 13 | -------------------------------------------------------------------------------- /core/runner/src/types/ParamsDictionary.ts: -------------------------------------------------------------------------------- 1 | export default interface ParamsDictionary { 2 | [key: string]: string; 3 | } 4 | -------------------------------------------------------------------------------- /core/runner/src/types/Properties.ts: -------------------------------------------------------------------------------- 1 | import type ParamsDictionary from "./ParamsDictionary"; 2 | 3 | type Properties = { 4 | properties: ParamsDictionary; 5 | }; 6 | 7 | export default Properties; 8 | -------------------------------------------------------------------------------- /core/runner/src/types/Targets.ts: -------------------------------------------------------------------------------- 1 | import type ResolverBase from "../ResolverBase"; 2 | 3 | type Targets = { 4 | [key: string]: ResolverBase; 5 | }; 6 | 7 | export default Targets; 8 | -------------------------------------------------------------------------------- /core/runner/src/types/Trigger.ts: -------------------------------------------------------------------------------- 1 | import type TriggerHttp from "./TriggerHttp"; 2 | 3 | type Trigger = { 4 | [key: string]: TriggerHttp; 5 | }; 6 | 7 | export default Trigger; 8 | -------------------------------------------------------------------------------- /core/runner/src/types/TriggerHttp.ts: -------------------------------------------------------------------------------- 1 | type TriggerHttp = { 2 | method: string; 3 | path: string; 4 | jwt_secret?: string; 5 | accept?: string; 6 | }; 7 | 8 | export default TriggerHttp; 9 | -------------------------------------------------------------------------------- /core/runner/src/types/TriggerResponse.ts: -------------------------------------------------------------------------------- 1 | import type { Context, MetricsType } from "@nanoservice-ts/shared"; 2 | 3 | type TriggerResponse = { 4 | ctx: Context; 5 | metrics: MetricsType; 6 | }; 7 | 8 | export default TriggerResponse; 9 | -------------------------------------------------------------------------------- /core/runner/src/types/Triggers.ts: -------------------------------------------------------------------------------- 1 | import type { Trigger } from "@nanoservice-ts/shared"; 2 | 3 | type Triggers = { 4 | [key: string]: Trigger; 5 | }; 6 | 7 | export default Triggers; 8 | -------------------------------------------------------------------------------- /core/runner/src/types/TryCatch.ts: -------------------------------------------------------------------------------- 1 | import type Flow from "./Flow"; 2 | 3 | type TryCatch = { 4 | try: Flow; 5 | catch: Flow; 6 | }; 7 | 8 | export default TryCatch; 9 | -------------------------------------------------------------------------------- /core/runner/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "rootDir": "./src", 6 | "declaration": true, 7 | "sourceMap": true, 8 | "outDir": "./dist", 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "noUnusedLocals": true, 13 | "noImplicitReturns": true, 14 | "skipLibCheck": true 15 | }, 16 | "compileOnSave": true, 17 | "include": ["./src"] 18 | } 19 | -------------------------------------------------------------------------------- /core/shared/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .env 4 | .env.local -------------------------------------------------------------------------------- /core/shared/.npmignore: -------------------------------------------------------------------------------- 1 | .github 2 | src 3 | node_modules -------------------------------------------------------------------------------- /core/shared/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @nanoservice-ts/shared 2 | 3 | ## 0.0.9 4 | 5 | ### Patch Changes 6 | 7 | - Python3 runtime implemented in the runner 8 | 9 | ## 0.0.8 10 | 11 | ### Patch Changes 12 | 13 | - Added examples and create project' command to include examples and 'create node' command with options for type ('module' or 'class') and template ('class' or 'ui') 14 | 15 | ## 0.0.7 16 | 17 | ### Patch Changes 18 | 19 | - Added support for YAML, XML and TOML in the workflow file. Upgraded package version recommended by Dependabot. 20 | 21 | ## 0.0.6 22 | 23 | ### Patch Changes 24 | 25 | - Updated the quickstart mdx and fixed api-call error issue with rest 26 | 27 | ## 0.0.5 28 | 29 | ### Patch Changes 30 | 31 | - Implemented a react node and the chatbot demo page 32 | 33 | ## 0.0.4 34 | 35 | ### Patch Changes 36 | 37 | - Improved Loki metrics 38 | 39 | ## 0.0.3 40 | 41 | ### Patch Changes 42 | 43 | - Improved and extended the open telemetry feature 44 | 45 | ## 0.0.2 46 | 47 | ### Patch Changes 48 | 49 | - Fixed open telemetry issues and types 50 | 51 | ## 0.0.1 52 | 53 | ### Patch Changes 54 | 55 | - Fixed issue with the cli node creation test 56 | -------------------------------------------------------------------------------- /core/shared/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts", 4 | "ignore": ["src/**/*.test.ts"], 5 | "exec": "npm run build && npx ts-node ./src/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /core/shared/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nanoservice-ts/shared", 3 | "version": "0.0.9", 4 | "description": "Shared class, interfaces and types", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "build:dev": "tsc --watch", 9 | "build": "rm -rf dist && npx tsc" 10 | }, 11 | "keywords": ["api", "blueprint"], 12 | "author": "Deskree Inc", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@types/lodash": "^4.14.196", 16 | "@types/node": "^22.13.4", 17 | "nodemon": "^3.1.9", 18 | "ts-node": "^10.9.1", 19 | "typescript": "^5.1.3" 20 | }, 21 | "dependencies": { 22 | "dayjs": "^1.11.10", 23 | "lodash": "^4.17.21" 24 | }, 25 | "private": false 26 | } 27 | -------------------------------------------------------------------------------- /core/shared/src/GlobalError.ts: -------------------------------------------------------------------------------- 1 | import type ErrorContext from "./types/ErrorContext"; 2 | import type ParamsDictionary from "./types/ParamsDictionary"; 3 | 4 | export default class GlobalError extends Error { 5 | public context: ErrorContext = { message: "" }; 6 | 7 | constructor(msg: string | undefined) { 8 | super(msg); 9 | Object.setPrototypeOf(this, GlobalError.prototype); 10 | 11 | this.context.message = msg as string; 12 | } 13 | 14 | setCode(code?: number) { 15 | this.context.code = code; 16 | } 17 | setJson(json?: Record) { 18 | this.context.json = json as ParamsDictionary; 19 | } 20 | setStack(stack?: string) { 21 | this.context.stack = stack; 22 | } 23 | setName(name?: string) { 24 | this.context.name = name; 25 | } 26 | 27 | hasJson(): boolean { 28 | return this.context.json !== undefined; 29 | } 30 | 31 | override toString(): string { 32 | if (this.context.json) return JSON.stringify(this.context.json); 33 | return this.context.message as string; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/shared/src/GlobalLogger.ts: -------------------------------------------------------------------------------- 1 | import type LoggerContext from "./types/LoggerContext"; 2 | 3 | export default abstract class GlobalLogger implements LoggerContext { 4 | protected logs: string[]; 5 | 6 | constructor() { 7 | this.logs = []; 8 | } 9 | 10 | abstract log(message: string): void; 11 | abstract logLevel(level: string, message: string): void; 12 | abstract error(message: string, stack: string): void; 13 | 14 | getLogs() { 15 | return this.logs; 16 | } 17 | 18 | getLogsAsText() { 19 | return this.logs.join("\n"); 20 | } 21 | 22 | getLogsAsBase64() { 23 | return Buffer.from(this.logs.join("\n")).toString("base64"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/shared/src/Metrics.ts: -------------------------------------------------------------------------------- 1 | import { CpuUsage, Time } from "./utils"; 2 | import MemoryUsage from "./utils/MemoryUsage"; 3 | import type { CpuUsageType, MemoryUsageType, TimeUsageType } from "./utils/MetricsBase"; 4 | 5 | class Metrics { 6 | private cpuUsage: CpuUsage; 7 | private memoryUsage: MemoryUsage; 8 | private time: Time; 9 | 10 | constructor() { 11 | this.cpuUsage = new CpuUsage(); 12 | this.memoryUsage = new MemoryUsage(); 13 | this.time = new Time(); 14 | } 15 | 16 | public start() { 17 | this.cpuUsage.start(); 18 | this.memoryUsage.start(); 19 | this.time.start(); 20 | } 21 | 22 | public retry() { 23 | this.memoryUsage.start(); 24 | } 25 | 26 | public stop() { 27 | this.cpuUsage.stop(); 28 | this.time.stop(); 29 | this.memoryUsage.stop(); 30 | } 31 | 32 | public clear() { 33 | this.memoryUsage.clear(); 34 | } 35 | 36 | public getMetrics(): MetricsType { 37 | const cpu = this.cpuUsage.getMetrics(); 38 | const memory = this.memoryUsage.getMetrics(); 39 | const time = this.time.getMetrics(); 40 | 41 | return { 42 | cpu, 43 | memory: memory, 44 | time, 45 | }; 46 | } 47 | } 48 | 49 | type MetricsType = { 50 | cpu: CpuUsageType; 51 | memory: MemoryUsageType; 52 | time: TimeUsageType; 53 | }; 54 | 55 | export { Metrics, type MetricsType }; 56 | -------------------------------------------------------------------------------- /core/shared/src/Trigger.ts: -------------------------------------------------------------------------------- 1 | type TriggerType = { 2 | listen(): void; 3 | }; 4 | 5 | export default abstract class Trigger implements TriggerType { 6 | abstract listen(): void; 7 | } 8 | -------------------------------------------------------------------------------- /core/shared/src/index.ts: -------------------------------------------------------------------------------- 1 | import GlobalError from "./GlobalError"; 2 | import GlobalLogger from "./GlobalLogger"; 3 | import { Metrics, type MetricsType } from "./Metrics"; 4 | import NodeBase from "./NodeBase"; 5 | import Trigger from "./Trigger"; 6 | import ConfigContext from "./types/ConfigContext"; 7 | import Context from "./types/Context"; 8 | import ErrorContext from "./types/ErrorContext"; 9 | import FunctionContext from "./types/FunctionContext"; 10 | import LoggerContext from "./types/LoggerContext"; 11 | import NodeConfigContext from "./types/NodeConfigContext"; 12 | import RequestContext from "./types/RequestContext"; 13 | import ResponseContext from "./types/ResponseContext"; 14 | import Step from "./types/Step"; 15 | import MemoryUsage from "./utils/MemoryUsage"; 16 | 17 | export { 18 | NodeBase, 19 | Context, 20 | RequestContext, 21 | ResponseContext, 22 | ErrorContext, 23 | LoggerContext, 24 | ConfigContext, 25 | Trigger, 26 | NodeConfigContext, 27 | FunctionContext, 28 | Step, 29 | GlobalLogger, 30 | GlobalError, 31 | Metrics, 32 | MemoryUsage, 33 | type MetricsType, 34 | }; 35 | -------------------------------------------------------------------------------- /core/shared/src/types/ConfigContext.ts: -------------------------------------------------------------------------------- 1 | import type NodeConfigContext from "./NodeConfigContext"; 2 | 3 | type ConfigContext = { 4 | nodes?: NodeConfigContext; 5 | }; 6 | 7 | export default ConfigContext; 8 | -------------------------------------------------------------------------------- /core/shared/src/types/Context.ts: -------------------------------------------------------------------------------- 1 | import type GlobalLogger from "../GlobalLogger"; 2 | import type ConfigContext from "./ConfigContext"; 3 | import type EnvContext from "./EnvContext"; 4 | import type ErrorContext from "./ErrorContext"; 5 | import type FunctionContext from "./FunctionContext"; 6 | import type LoggerContext from "./LoggerContext"; 7 | import type RequestContext from "./RequestContext"; 8 | import type ResponseContext from "./ResponseContext"; 9 | import type VarsContext from "./VarsContext"; 10 | 11 | type Context = { 12 | id: string; 13 | workflow_name?: string; 14 | workflow_path?: string; 15 | request: RequestContext; 16 | response: ResponseContext; 17 | error: ErrorContext; 18 | logger: LoggerContext; 19 | config: ConfigContext; 20 | func?: FunctionContext; 21 | vars?: VarsContext; 22 | env?: EnvContext; 23 | eventLogger: GlobalLogger | unknown; 24 | _PRIVATE_: unknown; 25 | }; 26 | 27 | export default Context; 28 | -------------------------------------------------------------------------------- /core/shared/src/types/EnvContext.ts: -------------------------------------------------------------------------------- 1 | import type ParamsDictionary from "./ParamsDictionary"; 2 | 3 | type EnvContext = { 4 | [key: string]: ParamsDictionary; 5 | }; 6 | export default EnvContext; 7 | -------------------------------------------------------------------------------- /core/shared/src/types/ErrorContext.ts: -------------------------------------------------------------------------------- 1 | import type ParamsDictionary from "./ParamsDictionary"; 2 | 3 | type ErrorContext = { 4 | message: string[] | string; 5 | code?: number; 6 | json?: ParamsDictionary; 7 | stack?: string; 8 | name?: string; 9 | }; 10 | 11 | export default ErrorContext; 12 | -------------------------------------------------------------------------------- /core/shared/src/types/FunctionContext.ts: -------------------------------------------------------------------------------- 1 | type FunctionContext = { 2 | [key: string]: unknown; 3 | }; 4 | 5 | export default FunctionContext; 6 | -------------------------------------------------------------------------------- /core/shared/src/types/LoggerContext.ts: -------------------------------------------------------------------------------- 1 | type LoggerContext = { 2 | log(message: string): void; 3 | getLogs(): string[]; 4 | getLogsAsText(): string; 5 | getLogsAsBase64(): string; 6 | logLevel(level: string, message: string): void; 7 | error(message: string, stack: string): void; 8 | }; 9 | 10 | export default LoggerContext; 11 | -------------------------------------------------------------------------------- /core/shared/src/types/NodeConfigContext.ts: -------------------------------------------------------------------------------- 1 | import type ParamsDictionary from "./ParamsDictionary"; 2 | 3 | type NodeConfigContext = { 4 | [key: string]: ParamsDictionary; 5 | }; 6 | 7 | export default NodeConfigContext; 8 | -------------------------------------------------------------------------------- /core/shared/src/types/ParamsDictionary.ts: -------------------------------------------------------------------------------- 1 | export default interface ParamsDictionary { 2 | [key: string]: string; 3 | } 4 | -------------------------------------------------------------------------------- /core/shared/src/types/RequestContext.ts: -------------------------------------------------------------------------------- 1 | import type ParamsDictionary from "./ParamsDictionary"; 2 | 3 | type RequestContext = { 4 | [key: string]: ParamsDictionary; 5 | }; 6 | 7 | export default RequestContext; 8 | -------------------------------------------------------------------------------- /core/shared/src/types/ResponseContext.ts: -------------------------------------------------------------------------------- 1 | import type BlueprintError from "../GlobalError"; 2 | 3 | type ResponseContext = { 4 | data: unknown; 5 | error: BlueprintError | null; 6 | success?: boolean; 7 | contentType?: string; 8 | }; 9 | 10 | export default ResponseContext; 11 | -------------------------------------------------------------------------------- /core/shared/src/types/Step.ts: -------------------------------------------------------------------------------- 1 | import type Context from "./Context"; 2 | 3 | type Step = { 4 | name: string; 5 | node: string; 6 | type: string; 7 | active?: boolean; 8 | stop?: boolean; 9 | run(ctx: Context): Promise; 10 | }; 11 | 12 | export default Step; 13 | -------------------------------------------------------------------------------- /core/shared/src/types/VarsContext.ts: -------------------------------------------------------------------------------- 1 | import type ParamsDictionary from "./ParamsDictionary"; 2 | 3 | type VarsContext = { 4 | [key: string]: ParamsDictionary; 5 | }; 6 | 7 | export default VarsContext; 8 | -------------------------------------------------------------------------------- /core/shared/src/utils/MemoryUsage.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import os from "node:os"; 3 | import MetricsBase from "./MetricsBase"; 4 | 5 | export default class MemoryUsage extends MetricsBase { 6 | protected min_val = 0; 7 | protected max_val = 0; 8 | protected total_val = 0; 9 | protected counter = 0; 10 | 11 | public start(): void { 12 | const usage = process.memoryUsage().heapUsed; 13 | const val = usage / 1000000; 14 | 15 | this.total_val += val; 16 | 17 | if (this.min_val === 0) this.min_val = val; 18 | this.min_val = this.min_val > val ? val : this.min_val; 19 | this.max_val = this.max_val < val ? val : this.max_val; 20 | 21 | this.counter++; 22 | } 23 | 24 | public stop() {} 25 | 26 | public getMetrics() { 27 | return { 28 | total: this.total_val / this.counter, 29 | min: this.min_val, 30 | max: this.max_val, 31 | global_memory: os.totalmem() / 1000000, 32 | global_free_memory: os.freemem() / 1000000, 33 | }; 34 | } 35 | 36 | public clear(): void { 37 | this.total_val = 0; 38 | this.min_val = 0; 39 | this.max_val = 0; 40 | this.counter = 0; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /core/shared/src/utils/MetricsBase.ts: -------------------------------------------------------------------------------- 1 | export default abstract class MetricsBase { 2 | public abstract start(ms?: number): void; 3 | public abstract stop(): void; 4 | public abstract getMetrics(): MemoryUsageType | TimeUsageType | CpuUsageType; 5 | } 6 | 7 | type MemoryUsageType = { 8 | total: number; 9 | min: number; 10 | max: number; 11 | global_memory: number; 12 | global_free_memory: number; 13 | }; 14 | 15 | type TimeUsageType = { 16 | startTime: string; 17 | endTime: string; 18 | duration: number; 19 | }; 20 | 21 | type CpuUsageType = { 22 | average: number; 23 | total: number; 24 | model: string; 25 | usage: number; 26 | }; 27 | 28 | export type { MemoryUsageType, TimeUsageType, CpuUsageType }; 29 | -------------------------------------------------------------------------------- /core/shared/src/utils/Time.ts: -------------------------------------------------------------------------------- 1 | import dayjs from "dayjs"; 2 | import MetricsBase, { type TimeUsageType } from "./MetricsBase"; 3 | 4 | export default class Time extends MetricsBase { 5 | private startTime: string | null = null; 6 | private endTime: string | null = null; 7 | private performanceStart = 0; 8 | private performanceEnd = 0; 9 | 10 | public start() { 11 | this.startTime = dayjs().format(); 12 | this.performanceStart = performance.now(); 13 | } 14 | 15 | public stop() { 16 | this.endTime = dayjs().format(); 17 | this.performanceEnd = performance.now(); 18 | } 19 | 20 | public getMetrics() { 21 | return { 22 | startTime: this.startTime, 23 | endTime: this.endTime, 24 | duration: this.performanceEnd - this.performanceStart, 25 | } as TimeUsageType; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/shared/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { default as CpuUsage } from "./CpuUsage"; 2 | export { default as Mapper } from "./Mapper"; 3 | export { default as MemoryUsage } from "./MemoryUsage"; 4 | export { default as Metrics } from "./MetricsBase"; 5 | export { default as Time } from "./Time"; 6 | -------------------------------------------------------------------------------- /core/shared/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "rootDir": "./src", 6 | "declaration": true, 7 | "sourceMap": true, 8 | "outDir": "./dist", 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "noUnusedLocals": true, 13 | "noImplicitReturns": true, 14 | "skipLibCheck": true 15 | }, 16 | "compileOnSave": true, 17 | "include": ["./src"] 18 | } 19 | -------------------------------------------------------------------------------- /core/workflow-helper/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @nanoservice/helper 2 | 3 | ## 0.1.5 4 | 5 | ### Patch Changes 6 | 7 | - added GRPC remote node execution server and client (NodeJS) 8 | 9 | ## 0.1.4 10 | 11 | ### Patch Changes 12 | 13 | - Added support for YAML, XML and TOML in the workflow file. Upgraded package version recommended by Dependabot. 14 | 15 | ## 0.1.3 16 | 17 | ### Patch Changes 18 | 19 | - Updated the imports with new scope 20 | 21 | ## 0.1.2 22 | 23 | ### Patch Changes 24 | 25 | - Changed the module scope to nanoservice-ts 26 | 27 | ## 0.1.1 28 | 29 | ### Patch Changes 30 | 31 | - Changed private to false 32 | 33 | ## 0.1.0 34 | 35 | ### Minor Changes 36 | 37 | - Nanoservice code modules initialized 38 | -------------------------------------------------------------------------------- /core/workflow-helper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nanoservice-ts/helper", 3 | "version": "0.1.5", 4 | "description": "", 5 | "engines": { 6 | "node": ">=18.0.0" 7 | }, 8 | "main": "dist/index.js", 9 | "types": "dist/index.d.ts", 10 | "files": ["dist"], 11 | "author": "Marco A. Castillo Della Sera", 12 | "license": "MIT", 13 | "scripts": { 14 | "build": "rm -rf dist && npx tsc", 15 | "build:dev": "tsc --watch", 16 | "test:dev": "vitest", 17 | "test": "vitest run", 18 | "typecheck": "tsc --noEmit --incremental" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "^22.13.4", 22 | "typescript": "^5.7.2", 23 | "vitest": "^3.1.4" 24 | }, 25 | "dependencies": { 26 | "zod": "^3.24.1" 27 | }, 28 | "private": false 29 | } 30 | -------------------------------------------------------------------------------- /core/workflow-helper/src/components/AddElse.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { type StepOpts, StepOptsSchema } from "../types/StepOpts"; 3 | 4 | export default class AddElse { 5 | private steps: StepOpts[] = []; 6 | 7 | addStep(step: StepOpts): AddElse { 8 | this.steps.push(step); 9 | return this; 10 | } 11 | 12 | build(): ConditionElseOpts { 13 | const model: ConditionElseOpts = { 14 | type: "else", 15 | steps: this.steps, 16 | }; 17 | 18 | ConditionElseSchema.parse(model); 19 | 20 | return model; 21 | } 22 | } 23 | 24 | const ConditionElseSchema = z.object({ 25 | type: z.enum(["if", "else"]), 26 | steps: z.array(StepOptsSchema).optional(), 27 | }); 28 | 29 | export type ConditionElseOpts = z.infer; 30 | -------------------------------------------------------------------------------- /core/workflow-helper/src/components/AddIf.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { type StepOpts, StepOptsSchema } from "../types/StepOpts"; 3 | 4 | export default class AddIf { 5 | private condition: string; 6 | private steps: StepOpts[] = []; 7 | 8 | constructor(condition: string) { 9 | this.condition = condition; 10 | } 11 | 12 | addStep(step: StepOpts): AddIf { 13 | this.steps.push(step); 14 | return this; 15 | } 16 | 17 | build(): ConditionOpts { 18 | const model: ConditionOpts = { 19 | type: "if", 20 | condition: this.condition, 21 | steps: this.steps, 22 | }; 23 | 24 | ConditionSchema.parse(model); 25 | 26 | return model; 27 | } 28 | } 29 | 30 | const ConditionSchema = z.object({ 31 | type: z.enum(["if", "else"]), 32 | condition: z 33 | .string({ 34 | required_error: "Condition is required", 35 | invalid_type_error: "Condition must be a string", 36 | }) 37 | .min(1), 38 | steps: z.array(StepOptsSchema).optional(), 39 | }); 40 | 41 | export type ConditionOpts = z.infer; 42 | -------------------------------------------------------------------------------- /core/workflow-helper/src/components/HelperResponse.ts: -------------------------------------------------------------------------------- 1 | import type { WorkflowOpts } from "../types/WorkflowOpts"; 2 | 3 | export default class HelperResponse { 4 | protected _config: WorkflowOpts = {}; 5 | 6 | setConfig(config: WorkflowOpts): void { 7 | this._config = config; 8 | } 9 | 10 | toJson(): string { 11 | return JSON.stringify(this._config); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/workflow-helper/src/components/Response.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/core/workflow-helper/src/components/Response.ts -------------------------------------------------------------------------------- /core/workflow-helper/src/components/Trigger.ts: -------------------------------------------------------------------------------- 1 | import { type TriggerOpts, TriggerOptsSchema, type TriggersEnum, TriggersSchema } from "../types/TriggerOpts"; 2 | import HelperResponse from "./HelperResponse"; 3 | import StepNode from "./StepNode"; 4 | 5 | export default class Trigger extends HelperResponse { 6 | addTrigger(name: TriggersEnum, config?: TriggerOpts): StepNode { 7 | TriggersSchema.parse(name); 8 | 9 | if ((config as unknown as string) === "http") { 10 | TriggerOptsSchema.parse(config); 11 | } 12 | this._config.trigger = { [name]: config || {} }; 13 | 14 | const helperResponse = new StepNode(); 15 | helperResponse.setConfig(this._config); 16 | return helperResponse; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/workflow-helper/src/components/Workflow.ts: -------------------------------------------------------------------------------- 1 | import { type WorkflowOpts, WorkflowOptsSchema } from "../types/WorkflowOpts"; 2 | import Trigger from "./Trigger"; 3 | 4 | export default function Workflow(config: WorkflowOpts): Trigger { 5 | WorkflowOptsSchema.parse(config); 6 | config.steps = []; 7 | config.nodes = {}; 8 | 9 | const helperResponse = new Trigger(); 10 | helperResponse.setConfig(config); 11 | return helperResponse; 12 | } 13 | -------------------------------------------------------------------------------- /core/workflow-helper/src/index.ts: -------------------------------------------------------------------------------- 1 | import AddElse from "./components/AddElse"; 2 | import AddIf, { ConditionOpts } from "./components/AddIf"; 3 | import HelperResponse from "./components/HelperResponse"; 4 | import Step from "./components/StepNode"; 5 | import Trigger from "./components/Trigger"; 6 | import Workflow from "./components/Workflow"; 7 | import { StepInputs, StepOpts } from "./types/StepOpts"; 8 | 9 | export { Workflow, Step, HelperResponse, Trigger, StepInputs, AddElse, AddIf, ConditionOpts, StepOpts }; 10 | -------------------------------------------------------------------------------- /core/workflow-helper/src/types/StepOpts.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import type { ConditionElseOpts } from "../components/AddElse"; 3 | import type { ConditionOpts } from "../components/AddIf"; 4 | 5 | export const StepOptsSchema = z.object({ 6 | name: z 7 | .string({ 8 | required_error: "Name is required", 9 | invalid_type_error: "Name must be a string", 10 | }) 11 | .min(3), 12 | node: z 13 | .string({ 14 | required_error: "Node is required", 15 | invalid_type_error: "Node must be a string", 16 | }) 17 | .min(5), 18 | type: z.enum(["local", "module", "runtime.python3"]), 19 | inputs: z.object({}).optional(), 20 | }); 21 | 22 | export type StepOpts = z.infer; 23 | 24 | // It is used globally in the project 25 | export const StepInputsSchema = z.object({}, { message: "Inputs required" }); 26 | export type StepInputs = z.infer; 27 | 28 | export const StepConditionSchema = z.object({ 29 | node: StepOptsSchema, 30 | conditions: z.function().optional(), 31 | }); 32 | 33 | export interface IConditions { 34 | conditions: () => ConditionOpts[] | ConditionElseOpts[]; 35 | } 36 | 37 | export type StepConditionOpts = z.infer; 38 | -------------------------------------------------------------------------------- /core/workflow-helper/src/types/TriggerOpts.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const TriggerOptsSchema = z.object({ 4 | method: z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "ANY"]), 5 | path: z.string().optional(), 6 | accept: z.string().default("application/json"), 7 | headers: z.record(z.string(), z.any()).optional(), 8 | }); 9 | 10 | export type TriggerOpts = z.infer; 11 | 12 | export const TriggersSchema = z.enum(["http", "cron", "manual", "grpc"]); 13 | export type TriggersEnum = z.infer; 14 | -------------------------------------------------------------------------------- /core/workflow-helper/src/types/WorkflowOpts.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { StepInputsSchema, StepOptsSchema } from "./StepOpts"; 3 | import { TriggerOptsSchema, TriggersSchema } from "./TriggerOpts"; 4 | 5 | export const WorkflowOptsSchema = z.object({ 6 | name: z 7 | .string({ 8 | required_error: "Name is required", 9 | invalid_type_error: "Name must be a string", 10 | }) 11 | .min(3), 12 | version: z 13 | .string({ 14 | required_error: "Version is required", 15 | invalid_type_error: "Version must be a string", 16 | }) 17 | .min(5, { message: "Format required x.x.x" }), 18 | description: z.string().optional(), 19 | steps: z.array(StepOptsSchema).optional(), 20 | nodes: z.record(z.string(), StepInputsSchema).optional(), 21 | trigger: z.record(TriggersSchema, TriggerOptsSchema).optional(), 22 | }); 23 | 24 | export type WorkflowOpts = z.infer; 25 | -------------------------------------------------------------------------------- /core/workflow-helper/tests/condition.test.ts: -------------------------------------------------------------------------------- 1 | import { beforeAll, test } from "vitest"; 2 | import AddElse from "../src/components/AddElse"; 3 | import AddIf from "../src/components/AddIf"; 4 | import type StepNode from "../src/components/StepNode"; 5 | import Workflow from "../src/components/Workflow"; 6 | 7 | let trigger: StepNode; 8 | 9 | beforeAll(() => { 10 | const workflow = Workflow({ 11 | name: "World Countries", 12 | version: "1.0.0", 13 | description: "Workflow description", 14 | }); 15 | 16 | trigger = workflow.addTrigger("http", { 17 | method: "GET", 18 | path: "/", 19 | accept: "application/json", 20 | }); 21 | }); 22 | 23 | test("Add condition", () => { 24 | trigger.addCondition({ 25 | node: { 26 | name: "filter-request", 27 | node: "control-flow/if-else@1.0.0", 28 | type: "local", 29 | }, 30 | conditions: () => { 31 | return [ 32 | new AddIf('ctx.request.query.countries === "true"') 33 | .addStep({ 34 | name: "get-countries", 35 | node: "@nanoservice/api-call", 36 | type: "module", 37 | inputs: { 38 | url: "https://countriesnow.space/api/v0.1/countries", 39 | method: "GET", 40 | headers: { 41 | "Content-Type": "application/json", 42 | }, 43 | responseType: "application/json", 44 | }, 45 | }) 46 | .build(), 47 | new AddElse() 48 | .addStep({ 49 | name: "get-facts", 50 | node: "@nanoservice/api-call", 51 | type: "module", 52 | inputs: { 53 | url: "https://catfact.ninja/fact", 54 | method: "GET", 55 | headers: { 56 | "Content-Type": "application/json", 57 | }, 58 | responseType: "application/json", 59 | }, 60 | }) 61 | .build(), 62 | ]; 63 | }, 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /core/workflow-helper/tests/step.test.ts: -------------------------------------------------------------------------------- 1 | import { beforeAll, expect, test } from "vitest"; 2 | import type StepNode from "../src/components/StepNode"; 3 | import Workflow from "../src/components/Workflow"; 4 | import { StepOptsSchema } from "../src/types/StepOpts"; 5 | let trigger: StepNode = {}; 6 | 7 | beforeAll(() => { 8 | const workflow = Workflow({ 9 | name: "World Countries", 10 | version: "1.0.0", 11 | description: "Workflow description", 12 | }); 13 | 14 | trigger = workflow.addTrigger("http", { 15 | method: "GET", 16 | path: "/", 17 | accept: "application/json", 18 | }); 19 | }); 20 | 21 | test("Initialize the workflow with inputs", (t) => { 22 | const step = trigger.addStep({ 23 | name: "get-countries-api", 24 | node: "@nanoservice/api-call", 25 | type: "module", 26 | inputs: { 27 | url: "https://countriesnow.space/api/v0.1/countries/capital", 28 | method: "GET", 29 | headers: { 30 | "Content-Type": "application/json", 31 | }, 32 | responseType: "application/json", 33 | }, 34 | }); 35 | 36 | const model = JSON.parse(step.toJson()).steps[0]; 37 | 38 | expect(() => StepOptsSchema.parse(model)).not.toThrow(); 39 | }); 40 | 41 | test("Initialize the workflow without inputs", (t) => { 42 | const step = trigger.addStep({ 43 | name: "get-countries-api", 44 | node: "@nanoservice/api-call", 45 | type: "module", 46 | }); 47 | 48 | const model = JSON.parse(step.toJson()).steps[0]; 49 | 50 | expect(() => StepOptsSchema.parse(model)).not.toThrow(); 51 | }); 52 | 53 | test("Initialize the workflow without inputs", (t) => { 54 | expect(() => 55 | trigger.addStep({ 56 | name: "", 57 | node: "@nanoservice/api-call", 58 | type: "module", 59 | }), 60 | ).toThrow(); 61 | }); 62 | -------------------------------------------------------------------------------- /core/workflow-helper/tests/workflow.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import HelperResponse from "../src/components/HelperResponse"; 3 | import Workflow from "../src/components/Workflow"; 4 | import { type WorkflowOpts, WorkflowOptsSchema } from "../src/types/WorkflowOpts"; 5 | 6 | test("Initialize the workflow with error", (t) => { 7 | expect(() => 8 | Workflow({ 9 | name: "", 10 | version: "", 11 | description: "", 12 | }), 13 | ).toThrow(); 14 | }); 15 | 16 | test("Initialize the workflow without error", (t) => { 17 | expect(() => 18 | Workflow({ 19 | name: "Workflow Demo", 20 | version: "1.0.0", 21 | description: "", 22 | }), 23 | ).not.toThrow(); 24 | }); 25 | 26 | test("Validate Response", (t) => { 27 | const response = Workflow({ 28 | name: "Workflow Demo", 29 | version: "1.0.0", 30 | description: "", 31 | }); 32 | 33 | expect(response).toBeInstanceOf(HelperResponse); 34 | }); 35 | 36 | test("Validate JSON", (t) => { 37 | const response = Workflow({ 38 | name: "Workflow Demo", 39 | version: "1.0.0", 40 | description: "", 41 | }); 42 | 43 | const jsonResponse = JSON.parse(response.toJson()) as WorkflowOpts; 44 | 45 | expect(() => WorkflowOptsSchema.parse(jsonResponse)).not.toThrow(); 46 | }); 47 | -------------------------------------------------------------------------------- /core/workflow-helper/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "rootDir": "./src", 6 | "types": ["node"], 7 | "declaration": true, 8 | "sourceMap": true, 9 | "outDir": "./dist", 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "noImplicitReturns": true, 15 | "skipLibCheck": true 16 | }, 17 | "compileOnSave": true, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /dockerfiles/Dockerfile: -------------------------------------------------------------------------------- 1 | # Base image with both Python and Node.js 2 | FROM python:3.11-slim-bookworm 3 | 4 | # Install Node.js (LTS version) 5 | RUN apt-get update && apt-get install -y --no-install-recommends \ 6 | curl \ 7 | && curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \ 8 | && apt-get install -y nodejs \ 9 | && rm -rf /var/lib/apt/lists/* 10 | 11 | # Install Supervisor 12 | RUN apt-get update && apt-get install -y --no-install-recommends \ 13 | supervisor \ 14 | git \ 15 | && rm -rf /var/lib/apt/lists/* 16 | 17 | # Set up working directories 18 | WORKDIR /app 19 | COPY . . 20 | 21 | 22 | # Install Python dependencies 23 | RUN if [ -f ./.nanoctl/runtimes/python3/requirements.txt ]; then \ 24 | pip install --no-cache-dir -r ./.nanoctl/runtimes/python3/requirements.txt; \ 25 | fi 26 | 27 | # Install Node.js dependencies 28 | RUN npm install 29 | # Build the Node.js application 30 | RUN npm run build 31 | 32 | # Configure Supervisor 33 | COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf 34 | 35 | # Set environment variables 36 | ENV WORKFLOWS_PATH=/app/workflows 37 | ENV CONSOLE_LOG_ACTIVE=true 38 | ENV NODE_ENV=production 39 | ENV APP_NAME=nanoservice-http 40 | 41 | # Expose ports (Node.js on 3000, Python on 8000) 42 | EXPOSE 4000/tcp 43 | EXPOSE 9091/tcp 44 | 45 | # Start Supervisor 46 | ENTRYPOINT ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf", "-e", "debug"] -------------------------------------------------------------------------------- /dockerfiles/Dockerfile.deploy.http: -------------------------------------------------------------------------------- 1 | # Base image with both Python and Node.js 2 | FROM python:3.11-slim-bookworm 3 | 4 | # Install Node.js (LTS version) 5 | RUN apt-get update && apt-get install -y --no-install-recommends \ 6 | curl \ 7 | && curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \ 8 | && apt-get install -y nodejs \ 9 | && rm -rf /var/lib/apt/lists/* 10 | 11 | # Install Supervisor 12 | RUN apt-get update && apt-get install -y --no-install-recommends \ 13 | supervisor \ 14 | git \ 15 | && rm -rf /var/lib/apt/lists/* 16 | 17 | # Set up working directories 18 | WORKDIR /app 19 | COPY . . 20 | 21 | 22 | # Install Python dependencies 23 | RUN if [ -f ./.nanoctl/runtimes/python3/requirements.txt ]; then \ 24 | pip install --no-cache-dir -r ./.nanoctl/runtimes/python3/requirements.txt; \ 25 | fi 26 | 27 | # Install Node.js dependencies 28 | RUN npm install 29 | # Build the Node.js application 30 | RUN npm run build 31 | 32 | # Configure Supervisor 33 | COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf 34 | 35 | # Set environment variables 36 | ENV WORKFLOWS_PATH=/app/workflows 37 | ENV CONSOLE_LOG_ACTIVE=true 38 | ENV NODE_ENV=production 39 | ENV APP_NAME=nanoservice-http 40 | 41 | # Expose ports (Node.js on 3000, Python on 8000) 42 | EXPOSE 4000/tcp 43 | EXPOSE 9091/tcp 44 | 45 | # Start Supervisor 46 | ENTRYPOINT ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf", "-e", "debug"] -------------------------------------------------------------------------------- /dockerfiles/Dockerfile.node: -------------------------------------------------------------------------------- 1 | # use the official Bun image 2 | # see all versions at https://hub.docker.com/r/oven/bun/tags 3 | FROM oven/bun:1 AS base 4 | WORKDIR /usr/src/app 5 | 6 | # install dependencies into temp directory 7 | # this will cache them and speed up future builds 8 | FROM base AS install 9 | RUN mkdir -p /temp/dev 10 | COPY package.json /temp/dev/ 11 | RUN cd /temp/dev && bun install --frozen-lockfile 12 | 13 | # install with --production (exclude devDependencies) 14 | RUN mkdir -p /temp/prod 15 | COPY package.json /temp/prod/ 16 | RUN cd /temp/prod && bun install --frozen-lockfile --production 17 | 18 | # copy node_modules from temp directory 19 | # then copy all (non-ignored) project files into the image 20 | FROM base AS prerelease 21 | COPY --from=install /temp/dev/node_modules node_modules 22 | COPY . . 23 | 24 | # [optional] tests & build 25 | ENV NODE_ENV=production 26 | # RUN bun add -d @types/ejs 27 | RUN bun run build 28 | 29 | # copy production dependencies and source code into final image 30 | FROM node:23.6.0-slim AS release 31 | WORKDIR /usr/src/app 32 | 33 | COPY --from=install /temp/prod/node_modules node_modules 34 | COPY --from=prerelease /usr/src/app/dist dist 35 | COPY --from=prerelease /usr/src/app/package.json . 36 | COPY --from=prerelease /usr/src/app/workflows workflows 37 | 38 | ENV WORKFLOWS_PATH=/usr/src/app/workflows 39 | ENV CONSOLE_LOG_ACTIVE=true 40 | ENV NODE_ENV=production 41 | ENV APP_NAME=nanoservice-http 42 | 43 | # run the app 44 | USER node 45 | EXPOSE 4000/tcp 46 | EXPOSE 9091/tcp 47 | 48 | #ENTRYPOINT [ "node", "-r", "./dist/opentelemetry_traces.js", "-r", "./dist/opentelemetry_metrics.js", "dist/index.js" ] 49 | ENTRYPOINT [ "node", "-r", "./dist/opentelemetry_metrics.js", "dist/index.js" ] -------------------------------------------------------------------------------- /docs/assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/docs/assets/.DS_Store -------------------------------------------------------------------------------- /docs/assets/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/assets/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/docs/assets/images/.DS_Store -------------------------------------------------------------------------------- /docs/assets/images/ai-database-query.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/docs/assets/images/ai-database-query.jpeg -------------------------------------------------------------------------------- /docs/assets/images/nanoservices-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/docs/assets/images/nanoservices-dark.png -------------------------------------------------------------------------------- /docs/assets/images/nanoservices-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/docs/assets/images/nanoservices-light.png -------------------------------------------------------------------------------- /docs/assets/logo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/docs/assets/logo/.DS_Store -------------------------------------------------------------------------------- /docs/c/devtools/index.mdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/docs/c/devtools/index.mdx -------------------------------------------------------------------------------- /docs/c/migration-guides/index.mdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/docs/c/migration-guides/index.mdx -------------------------------------------------------------------------------- /docs/d/cli/build.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Build 3 | --- 4 | 5 | The `nanoctl build` command compiles your nanoservice source code into a deployable artifact, preparing it for execution or deployment. This step ensures your code, dependencies, and configuration are packaged correctly according to the `nanoservice-ts` project structure. 6 | 7 | Whether you're building from the default project directory, a custom location, or directly within the current folder, the CLI offers flexible options to match your development workflow. Use it as part of your standard development, testing, or deployment process. 8 | 9 | Once the build is complete, you're ready to deploy your nanoservice using the [`deploy`](./Deployment.md) command. 10 | 11 | ## Build Nanoservice 12 | 13 | ### Syntax 14 | ```bash 15 | npx nanoctl build [options] 16 | ``` 17 | Compiles and packages a nanoservice from source code into a deployable artifact. 18 | 19 | ### Options 20 | | Option | Alias | Type | Description | Default | 21 | |--------------|-------|---------|----------------------------|--------------------| 22 | | `--directory`| `-d` | string | Source directory path | `./nanoservice-ts` | 23 | | `--help` | `-h` | boolean | Show help | `false` | 24 | 25 | ### Commands 26 | | Command | Description | 27 | |---------|---------------------------| 28 | | `.` | Build in current directory| 29 | 30 | ### Examples 31 | #### Build in default directory: 32 | ``` bash 33 | npx nanoctl build 34 | ``` 35 | #### Build in specific directory: 36 | ```bash 37 | npx nanoctl build -d ./my-nanoservice 38 | ``` 39 | 40 | #### Build in current directory: 41 | ```bash 42 | npx nanoctl build . 43 | ``` -------------------------------------------------------------------------------- /docs/o/support/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Support Us 3 | --- 4 | 5 | # Support Nanoservice-TS 6 | 7 | We appreciate your interest in Nanoservice-TS! Your support helps us grow and improve the framework. 8 | 9 | ## How You Can Support 10 | 11 | * **Contribute to the Project**: We welcome contributions to the Nanoservice-TS codebase, documentation, or examples. Check out our [Contributing Guide](https://github.com/deskree-inc/nanoservice-ts/blob/main/CONTRIBUTING.md) (link to be confirmed or created in the repository) for more details. 12 | * **Report Issues & Request Features**: If you encounter any bugs, have suggestions for improvements, or would like to request new features, please open an issue on our GitHub repository. 13 | * **[Submit an Issue on GitHub](https://github.com/deskree-inc/nanoservice-ts/issues)** 14 | * **Spread the Word**: Tell your friends, colleagues, and the developer community about Nanoservice-TS. 15 | * **Share Your Projects**: If you build something cool with Nanoservice-TS, let us know! We love to see how the community is using the framework. 16 | 17 | Thank you for being a part of the Nanoservice-TS community! 18 | -------------------------------------------------------------------------------- /docs/ref/.nojekyll: -------------------------------------------------------------------------------- 1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. -------------------------------------------------------------------------------- /docs/ref/README.md: -------------------------------------------------------------------------------- 1 | nanoservice-ts 2 | 3 | # nanoservice-ts - v0.1.0 4 | 5 | ## Table of contents 6 | 7 | ### Modules 8 | 9 | - [runner/src](modules/runner_src.md) 10 | - [shared/src](modules/shared_src.md) 11 | -------------------------------------------------------------------------------- /docs/ref/classes/runner_src.ResolverBase.md: -------------------------------------------------------------------------------- 1 | [nanoservice-ts - v0.1.0](../README.md) / [runner/src](../modules/runner_src.md) / ResolverBase 2 | 3 | # Class: ResolverBase 4 | 5 | [runner/src](../modules/runner_src.md).ResolverBase 6 | 7 | ## Hierarchy 8 | 9 | - **`ResolverBase`** 10 | 11 | ↳ [`LocalStorage`](runner_src.LocalStorage.md) 12 | 13 | ## Table of contents 14 | 15 | ### Constructors 16 | 17 | - [constructor](runner_src.ResolverBase.md#constructor) 18 | 19 | ### Methods 20 | 21 | - [createContext](runner_src.ResolverBase.md#createcontext) 22 | - [get](runner_src.ResolverBase.md#get) 23 | 24 | ## Constructors 25 | 26 | ### constructor 27 | 28 | • **new ResolverBase**(): [`ResolverBase`](runner_src.ResolverBase.md) 29 | 30 | #### Returns 31 | 32 | [`ResolverBase`](runner_src.ResolverBase.md) 33 | 34 | ## Methods 35 | 36 | ### createContext 37 | 38 | ▸ **createContext**(`logger?`): `Context` 39 | 40 | #### Parameters 41 | 42 | | Name | Type | 43 | | :------ | :------ | 44 | | `logger?` | `any` | 45 | 46 | #### Returns 47 | 48 | `Context` 49 | 50 | #### Defined in 51 | 52 | [core/runner/src/ResolverBase.ts:9](https://github.com/deskree-inc/nanoservice-ts/blob/fd59582/core/runner/src/ResolverBase.ts#L9) 53 | 54 | ___ 55 | 56 | ### get 57 | 58 | ▸ **get**(`name`, `workflow`): `Promise`\<[`Config`](../modules/runner_src.md#config)\> 59 | 60 | #### Parameters 61 | 62 | | Name | Type | 63 | | :------ | :------ | 64 | | `name` | `string` | 65 | | `workflow` | `WorkflowLocator` | 66 | 67 | #### Returns 68 | 69 | `Promise`\<[`Config`](../modules/runner_src.md#config)\> 70 | 71 | #### Defined in 72 | 73 | [core/runner/src/ResolverBase.ts:7](https://github.com/deskree-inc/nanoservice-ts/blob/fd59582/core/runner/src/ResolverBase.ts#L7) 74 | -------------------------------------------------------------------------------- /docs/ref/classes/runner_src.RunnerSteps.md: -------------------------------------------------------------------------------- 1 | [nanoservice-ts - v0.1.0](../README.md) / [runner/src](../modules/runner_src.md) / RunnerSteps 2 | 3 | # Class: RunnerSteps 4 | 5 | [runner/src](../modules/runner_src.md).RunnerSteps 6 | 7 | ## Hierarchy 8 | 9 | - **`RunnerSteps`** 10 | 11 | ↳ [`Runner`](runner_src.Runner.md) 12 | 13 | ## Table of contents 14 | 15 | ### Constructors 16 | 17 | - [constructor](runner_src.RunnerSteps.md#constructor) 18 | 19 | ### Methods 20 | 21 | - [runSteps](runner_src.RunnerSteps.md#runsteps) 22 | 23 | ## Constructors 24 | 25 | ### constructor 26 | 27 | • **new RunnerSteps**(): [`RunnerSteps`](runner_src.RunnerSteps.md) 28 | 29 | #### Returns 30 | 31 | [`RunnerSteps`](runner_src.RunnerSteps.md) 32 | 33 | ## Methods 34 | 35 | ### runSteps 36 | 37 | ▸ **runSteps**(`ctx`, `steps`, `deep?`, `step_name?`): `Promise`\<`Context`\> 38 | 39 | Executes a series of steps in the given context. 40 | 41 | #### Parameters 42 | 43 | | Name | Type | Default value | Description | 44 | | :------ | :------ | :------ | :------ | 45 | | `ctx` | `Context` | `undefined` | The context in which the steps are executed. | 46 | | `steps` | `NodeBase`[] | `undefined` | An array of NanoService steps to be executed. | 47 | | `deep` | `boolean` | `false` | A boolean indicating whether the function is being called recursively for flow steps. | 48 | | `step_name` | `string` | `""` | The name of the current step being processed in a flow. | 49 | 50 | #### Returns 51 | 52 | `Promise`\<`Context`\> 53 | 54 | A promise that resolves to the updated context after all steps have been executed. 55 | 56 | **`Throws`** 57 | 58 | Throws a GlobalError if any step results in an error. 59 | 60 | #### Defined in 61 | 62 | [core/runner/src/RunnerSteps.ts:15](https://github.com/deskree-inc/nanoservice-ts/blob/fd59582/core/runner/src/RunnerSteps.ts#L15) 63 | -------------------------------------------------------------------------------- /docs/ref/classes/shared_src.Trigger.md: -------------------------------------------------------------------------------- 1 | [nanoservice-ts - v0.1.0](../README.md) / [shared/src](../modules/shared_src.md) / Trigger 2 | 3 | # Class: Trigger 4 | 5 | [shared/src](../modules/shared_src.md).Trigger 6 | 7 | ## Implements 8 | 9 | - `TriggerType` 10 | 11 | ## Table of contents 12 | 13 | ### Constructors 14 | 15 | - [constructor](shared_src.Trigger.md#constructor) 16 | 17 | ### Methods 18 | 19 | - [listen](shared_src.Trigger.md#listen) 20 | 21 | ## Constructors 22 | 23 | ### constructor 24 | 25 | • **new Trigger**(): [`Trigger`](shared_src.Trigger.md) 26 | 27 | #### Returns 28 | 29 | [`Trigger`](shared_src.Trigger.md) 30 | 31 | ## Methods 32 | 33 | ### listen 34 | 35 | ▸ **listen**(): `void` 36 | 37 | #### Returns 38 | 39 | `void` 40 | 41 | #### Implementation of 42 | 43 | TriggerType.listen 44 | 45 | #### Defined in 46 | 47 | [core/shared/src/Trigger.ts:6](https://github.com/deskree-inc/nanoservice-ts/blob/fd59582/core/shared/src/Trigger.ts#L6) 48 | -------------------------------------------------------------------------------- /docs/ref/interfaces/runner_src.INanoServiceResponse.md: -------------------------------------------------------------------------------- 1 | [nanoservice-ts - v0.1.0](../README.md) / [runner/src](../modules/runner_src.md) / INanoServiceResponse 2 | 3 | # Interface: INanoServiceResponse 4 | 5 | [runner/src](../modules/runner_src.md).INanoServiceResponse 6 | 7 | ## Hierarchy 8 | 9 | - `unknown` 10 | 11 | ↳ **`INanoServiceResponse`** 12 | 13 | ## Implemented by 14 | 15 | - [`NanoServiceResponse`](../classes/runner_src.NanoServiceResponse.md) 16 | 17 | ## Table of contents 18 | 19 | ### Properties 20 | 21 | - [steps](runner_src.INanoServiceResponse.md#steps) 22 | 23 | ## Properties 24 | 25 | ### steps 26 | 27 | • **steps**: `NodeBase`[] 28 | 29 | #### Defined in 30 | 31 | [core/runner/src/NanoServiceResponse.ts:5](https://github.com/deskree-inc/nanoservice-ts/blob/fd59582/core/runner/src/NanoServiceResponse.ts#L5) 32 | -------------------------------------------------------------------------------- /docs/ref/interfaces/runner_src.JsonLikeObject.md: -------------------------------------------------------------------------------- 1 | [nanoservice-ts - v0.1.0](../README.md) / [runner/src](../modules/runner_src.md) / JsonLikeObject 2 | 3 | # Interface: JsonLikeObject 4 | 5 | [runner/src](../modules/runner_src.md).JsonLikeObject 6 | 7 | ## Indexable 8 | 9 | ▪ [key: `string`]: `string` \| `number` \| `boolean` \| [`JsonLikeObject`](runner_src.JsonLikeObject.md) \| [`JsonLikeObject`](runner_src.JsonLikeObject.md)[] 10 | -------------------------------------------------------------------------------- /docs/ref/interfaces/runner_src.ParamsDictionary.md: -------------------------------------------------------------------------------- 1 | [nanoservice-ts - v0.1.0](../README.md) / [runner/src](../modules/runner_src.md) / ParamsDictionary 2 | 3 | # Interface: ParamsDictionary 4 | 5 | [runner/src](../modules/runner_src.md).ParamsDictionary 6 | 7 | ## Indexable 8 | 9 | ▪ [key: `string`]: `string` 10 | -------------------------------------------------------------------------------- /infra/development/3059.dat: -------------------------------------------------------------------------------- 1 | 1 Action 2006-02-15 09:46:27 2 | 2 Animation 2006-02-15 09:46:27 3 | 3 Children 2006-02-15 09:46:27 4 | 4 Classics 2006-02-15 09:46:27 5 | 5 Comedy 2006-02-15 09:46:27 6 | 6 Documentary 2006-02-15 09:46:27 7 | 7 Drama 2006-02-15 09:46:27 8 | 8 Family 2006-02-15 09:46:27 9 | 9 Foreign 2006-02-15 09:46:27 10 | 10 Games 2006-02-15 09:46:27 11 | 11 Horror 2006-02-15 09:46:27 12 | 12 Music 2006-02-15 09:46:27 13 | 13 New 2006-02-15 09:46:27 14 | 14 Sci-Fi 2006-02-15 09:46:27 15 | 15 Sports 2006-02-15 09:46:27 16 | 16 Travel 2006-02-15 09:46:27 17 | \. 18 | 19 | 20 | -------------------------------------------------------------------------------- /infra/development/3073.dat: -------------------------------------------------------------------------------- 1 | 1 English 2006-02-15 10:02:19 2 | 2 Italian 2006-02-15 10:02:19 3 | 3 Japanese 2006-02-15 10:02:19 4 | 4 Mandarin 2006-02-15 10:02:19 5 | 5 French 2006-02-15 10:02:19 6 | 6 German 2006-02-15 10:02:19 7 | \. 8 | 9 | 10 | -------------------------------------------------------------------------------- /infra/development/3079.dat: -------------------------------------------------------------------------------- 1 | 1 Mike Hillyer 3 Mike.Hillyer@sakilastaff.com 1 t Mike 8cb2237d0679ca88db6464eac60da96345513964 2006-05-16 16:13:11.79328 \\x89504e470d0a5a0a 2 | 2 Jon Stephens 4 Jon.Stephens@sakilastaff.com 2 t Jon 8cb2237d0679ca88db6464eac60da96345513964 2006-05-16 16:13:11.79328 \N 3 | \. 4 | 5 | 6 | -------------------------------------------------------------------------------- /infra/development/3081.dat: -------------------------------------------------------------------------------- 1 | 1 1 1 2006-02-15 09:57:12 2 | 2 2 2 2006-02-15 09:57:12 3 | \. 4 | 5 | 6 | -------------------------------------------------------------------------------- /infra/development/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: postgres 4 | container_name: postgres 5 | restart: always 6 | shm_size: 128mb 7 | environment: 8 | POSTGRES_USER: postgres 9 | POSTGRES_PASSWORD: example 10 | ports: 11 | - 5432:5432 12 | volumes: 13 | - ./schema.sql:/docker-entrypoint-initdb.d/schema.sql # DB dvdrental 14 | - ./:/var/lib/postgresql/bump 15 | networks: 16 | - shared-network 17 | adminer: 18 | image: adminer 19 | container_name: adminer 20 | restart: always 21 | ports: 22 | - 8080:8080 23 | networks: 24 | - shared-network 25 | 26 | networks: 27 | shared-network: 28 | external: true 29 | 30 | # docker network create shared-network -------------------------------------------------------------------------------- /infra/development/toc.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/infra/development/toc.dat -------------------------------------------------------------------------------- /infra/metrics/datasources.yml: -------------------------------------------------------------------------------- 1 | datasources: 2 | - name: prometheus 3 | type: prometheus 4 | url: http://prometheus:9090 5 | access: proxy 6 | isDefault: true 7 | - name: Loki 8 | type: loki 9 | access: proxy 10 | url: http://loki:3100 11 | jsonData: 12 | timeout: 60 13 | maxLines: 1000 14 | # - name: Tempo 15 | # type: tempo 16 | # access: proxy 17 | # url: http://tempo:3200 18 | # basicAuth: false 19 | # isDefault: false 20 | # version: 1 21 | # editable: false 22 | # apiVersion: 1 23 | # uid: tempo 24 | # jsonData: 25 | # httpMethod: GET 26 | # serviceMap: 27 | # datasourceUid: prometheus 28 | # streamingEnabled: 29 | # search: true -------------------------------------------------------------------------------- /infra/metrics/default.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: Default # A uniquely identifiable name for the provider 5 | folder: Nanoservice # The folder where to place the dashboards 6 | type: file 7 | options: 8 | path: /var/lib/grafana/dashboards/dashboard.json -------------------------------------------------------------------------------- /infra/metrics/loki-config.yaml: -------------------------------------------------------------------------------- 1 | auth_enabled: false 2 | 3 | server: 4 | http_listen_port: 3100 5 | grpc_listen_port: 9096 6 | 7 | common: 8 | instance_addr: 127.0.0.1 9 | path_prefix: /tmp/loki 10 | storage: 11 | filesystem: 12 | chunks_directory: /tmp/loki/chunks 13 | rules_directory: /tmp/loki/rules 14 | replication_factor: 1 15 | ring: 16 | kvstore: 17 | store: inmemory 18 | 19 | query_range: 20 | results_cache: 21 | cache: 22 | embedded_cache: 23 | enabled: true 24 | max_size_mb: 100 25 | 26 | schema_config: 27 | configs: 28 | - from: 2020-10-24 29 | store: tsdb 30 | object_store: filesystem 31 | schema: v13 32 | index: 33 | prefix: index_ 34 | period: 24h 35 | 36 | ruler: 37 | alertmanager_url: http://localhost:9093 38 | 39 | # By default, Loki will send anonymous, but uniquely-identifiable usage and configuration 40 | # analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/ 41 | # 42 | # Statistics help us better understand how Loki is used, and they show us performance 43 | # levels for most users. This helps us prioritize features and documentation. 44 | # For more information on what's sent, look at 45 | # https://github.com/grafana/loki/blob/main/pkg/analytics/stats.go 46 | # Refer to the buildReport method to see what goes into a report. 47 | # 48 | # If you would like to disable reporting, uncomment the following lines: 49 | #analytics: 50 | # reporting_enabled: false -------------------------------------------------------------------------------- /infra/metrics/nginx.conf: -------------------------------------------------------------------------------- 1 | events {} 2 | 3 | http { 4 | server { 5 | listen 80; 6 | 7 | location /loki/ { 8 | proxy_pass http://loki:3100; 9 | proxy_set_header Host $host; 10 | 11 | # CORS headers 12 | add_header Access-Control-Allow-Origin *; 13 | add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; 14 | add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; 15 | add_header Access-Control-Expose-Headers 'Content-Length,Content-Range'; 16 | 17 | if ($request_method = 'OPTIONS') { 18 | add_header Access-Control-Max-Age 1728000; 19 | add_header Content-Type 'text/plain; charset=utf-8'; 20 | add_header Content-Length 0; 21 | return 204; 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /infra/metrics/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 5s #Scrape interval to every 15 seconds. 3 | evaluation_interval: 5s #Evaluate rules every 15 seconds. 4 | 5 | scrape_configs: 6 | - job_name: "prometheus" 7 | static_configs: 8 | - targets: ["prometheus:9090"] 9 | 10 | - job_name: "cadvisor" 11 | static_configs: 12 | - targets: ["cadvisor:8080"] 13 | 14 | - job_name: "nanoservice-http" 15 | static_configs: 16 | - targets: ["nanoservice-http:4000"] 17 | 18 | # - job_name: 'nanoservice-http' 19 | # static_configs: 20 | # - targets: ['telegraf:9273'] -------------------------------------------------------------------------------- /infra/metrics/telegraf.conf: -------------------------------------------------------------------------------- 1 | [agent] 2 | interval = "10s" 3 | round_interval = true 4 | metric_batch_size = 1000 5 | metric_buffer_limit = 10000 6 | collection_jitter = "0s" 7 | flush_interval = "10s" 8 | flush_jitter = "0s" 9 | precision = "" 10 | hostname = "" 11 | omit_hostname = true 12 | 13 | ############################################################################### 14 | # OUTPUTS 15 | ############################################################################### 16 | 17 | [[outputs.prometheus_client]] 18 | listen = ":9273" 19 | metric_version = 2 20 | path = "/metrics" 21 | expiration_interval = "60s" 22 | 23 | ############################################################################### 24 | # INPUTS 25 | ############################################################################### 26 | 27 | # [[inputs.docker]] 28 | # endpoint = "tcp://docker-proxy:2375" 29 | # gather_services = false 30 | # container_names = [] 31 | # timeout = "5s" 32 | # perdevice = true 33 | # total = true 34 | # docker_label_include = [] 35 | # docker_label_exclude = [] 36 | # tag_env = ["JAVA_HOME", "MY_CUSTOM_TAG"] 37 | 38 | # [[inputs.prometheus]] 39 | # urls = [ 40 | # "http://nanoservice-http:9091/metrics" 41 | # ] -------------------------------------------------------------------------------- /infra/milvus/.gitignore: -------------------------------------------------------------------------------- 1 | volumes -------------------------------------------------------------------------------- /nodes/control-flow/if-else@1.0.0/README.md: -------------------------------------------------------------------------------- 1 | # Configuration Options 2 | The IfElse node allows you to have a conditional statement and call other nodes depending on the conditions 3 | 4 | ## Node properties 5 | 6 | ### Required properties 7 | - `conditions` (array): List of conditions to check 8 | - `type` (string if | else): type of the condition 9 | - `condition` (string): The condition string to be checked 10 | - `steps` (array): List of steps to execute if the condition is true 11 | 12 | 13 | ## Usage/Examples 14 | ### Step Configuration 15 | 16 | ```json 17 | { 18 | "name": "if-else", 19 | "node": "if-else@1.0.0", 20 | "type": "local" 21 | } 22 | ``` 23 | 24 | ### Node Configuration 25 | 26 | 27 | ```json 28 | "if-else": { 29 | "conditions": [ 30 | { 31 | "type": "if", 32 | "condition": "data !== undefined", 33 | "steps": [] 34 | }, 35 | { 36 | "type": "else", 37 | "steps": [] 38 | } 39 | ] 40 | } 41 | ``` 42 | 43 | -------------------------------------------------------------------------------- /nodes/control-flow/if-else@1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import type { ConditionOpts } from "@nanoservice-ts/helper"; 2 | import { type Condition, type INanoServiceResponse, NanoService } from "@nanoservice-ts/runner"; 3 | import type { Context, NodeBase } from "@nanoservice-ts/shared"; 4 | import type ParamsDictionary from "@nanoservice-ts/shared/dist/types/ParamsDictionary"; 5 | 6 | export default class IfElse extends NanoService> { 7 | constructor() { 8 | super(); 9 | this.flow = true; 10 | this.contentType = ""; 11 | } 12 | 13 | async handle(ctx: Context, inputs: Array): Promise[]> { 14 | let steps: NodeBase[] = []; 15 | const conditions = inputs; 16 | 17 | const firstCondition = conditions[0] as ConditionOpts; 18 | if (firstCondition.type !== "if") throw new Error("First condition must be an if"); 19 | 20 | if (conditions.length > 1) { 21 | const lastCondition = conditions[conditions.length - 1]; 22 | if (lastCondition.type !== "else") throw new Error("Last condition must be an else"); 23 | } 24 | 25 | for (let i = 0; i < conditions.length; i++) { 26 | const condition = conditions[i]; 27 | 28 | if (condition.condition !== undefined && condition.condition.trim() !== "") { 29 | const result = this.runJs(condition.condition, ctx, ctx.response.data as ParamsDictionary, {}, ctx.vars); 30 | 31 | if (result) { 32 | steps = condition.steps as NodeBase[]; 33 | break; 34 | } 35 | } else { 36 | steps = condition.steps as NodeBase[]; 37 | break; 38 | } 39 | } 40 | 41 | return steps as unknown as NanoService[]; 42 | } 43 | } 44 | 45 | type NodeOptions = { 46 | conditions: Condition[]; 47 | }; 48 | 49 | export type { NodeOptions }; 50 | -------------------------------------------------------------------------------- /nodes/control-flow/if-else@1.0.0/inputSchema.ts: -------------------------------------------------------------------------------- 1 | export const inputSchema = { 2 | $schema: "http://json-schema.org/draft-07/schema#", 3 | title: "Generated schema for Root", 4 | type: "array", 5 | items: { 6 | type: "object", 7 | properties: { 8 | type: { 9 | type: "string", 10 | }, 11 | steps: { 12 | type: "array", 13 | items: { 14 | type: "object", 15 | properties: { 16 | name: { 17 | type: "string", 18 | }, 19 | node: { 20 | type: "string", 21 | }, 22 | type: { 23 | type: "string", 24 | }, 25 | }, 26 | required: ["name", "node", "type"], 27 | }, 28 | }, 29 | condition: { 30 | type: "string", 31 | }, 32 | }, 33 | required: ["type", "steps"], 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /nodes/control-flow/if-else@1.0.0/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": ".ts,.js", 4 | "ignore": [], 5 | "exec": "pnpm --filter @nanoservice-ts/if-else run build" 6 | } 7 | -------------------------------------------------------------------------------- /nodes/control-flow/if-else@1.0.0/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nanoservice-ts/if-else", 3 | "version": "0.0.30", 4 | "description": "nanoservice if-else", 5 | "engines": { 6 | "node": ">=18.0.0" 7 | }, 8 | "author": "Deskree Technologies Inc.", 9 | "license": "Apache-2.0", 10 | "main": "dist/index.js", 11 | "types": "dist/index.d.ts", 12 | "scripts": { 13 | "start:dev": "npx nodemon", 14 | "build": "rimraf ./dist && tsc", 15 | "build:dev": "tsc --watch", 16 | "test": "vitest run", 17 | "test:dev": "vitest --watch" 18 | }, 19 | "devDependencies": { 20 | "@types/lodash": "^4.14.196", 21 | "@types/node": "^22.13.4", 22 | "nodemon": "^3.1.9", 23 | "rimraf": "^6.0.1", 24 | "ts-node": "^10.9.1", 25 | "typescript": "^5.1.3", 26 | "vitest": "^3.1.4" 27 | }, 28 | "dependencies": { 29 | "@nanoservice-ts/helper": "workspace:*", 30 | "@nanoservice-ts/runner": "workspace:*", 31 | "@nanoservice-ts/shared": "workspace:*", 32 | "lodash": "^4.17.21" 33 | }, 34 | "private": false 35 | } 36 | -------------------------------------------------------------------------------- /nodes/control-flow/if-else@1.0.0/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "sourceMap": true, 7 | "outDir": "./dist", 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "strict": true, 11 | "noUnusedLocals": true, 12 | "noImplicitReturns": true, 13 | "skipLibCheck": true 14 | }, 15 | "compileOnSave": true 16 | } 17 | -------------------------------------------------------------------------------- /nodes/web/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/nodes/web/.DS_Store -------------------------------------------------------------------------------- /nodes/web/api-call@1.0.0/README.md: -------------------------------------------------------------------------------- 1 | # Configuration Options 2 | The ApiCall node allows you to make an API call to any API endpoint 3 | 4 | 5 | ## Node properties 6 | 7 | ### Required properties 8 | - `url *` (string): A string representing the MongoDB connection string. (Required) 9 | - `method *` (string): The name of the MongoDB database to delete. (Required) 10 | 11 | ### Optional properties 12 | 13 | - `headers` (Object): The headers of the request. 14 | - `body` (object): the body of the request. 15 | - `responseType` (string): the response type of the request. 16 | 17 | ## Usage/Examples 18 | ### Step Configuration 19 | 20 | ```json 21 | { 22 | "name": "api-call", 23 | "node": "api-call@1.0.0", 24 | "type": "local" 25 | } 26 | ``` 27 | 28 | ### Node Configuration 29 | ```json 30 | "api-call": { 31 | "inputs": { 32 | "properties": { 33 | "url": "https://bp-firestore-stack.api-dev.deskree.com/api/v1/auth/accounts/sign-in/email", 34 | "method": "POST", 35 | "headers": { 36 | "Content-Type": "application/json", 37 | "Authorization": "Bearer ${ctx.vars.authApiResponse.data.idToken}" 38 | }, 39 | "body": { 40 | "email": "email@email.com", 41 | "password": "123password" 42 | } 43 | } 44 | } 45 | } 46 | ``` 47 | 48 | -------------------------------------------------------------------------------- /nodes/web/api-call@1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type INanoServiceResponse, 3 | type JsonLikeObject, 4 | NanoService, 5 | NanoServiceResponse, 6 | } from "@nanoservice-ts/runner"; 7 | import { type Context, GlobalError } from "@nanoservice-ts/shared"; 8 | import { inputSchema } from "./inputSchema"; 9 | import { runApiCall } from "./util"; 10 | 11 | export type InputType = { 12 | method: string; 13 | url: string; 14 | headers: JsonLikeObject; 15 | responseType: string; 16 | body: JsonLikeObject; 17 | }; 18 | 19 | export default class ApiCall extends NanoService { 20 | constructor() { 21 | super(); 22 | 23 | this.inputSchema = inputSchema; 24 | this.outputSchema = {}; 25 | } 26 | 27 | async handle(ctx: Context, inputs: InputType): Promise { 28 | const response: NanoServiceResponse = new NanoServiceResponse(); 29 | 30 | try { 31 | const method = inputs.method; 32 | const url = inputs.url; 33 | const headers = inputs.headers; 34 | const responseType = inputs.responseType; 35 | const body = inputs.body || ctx.response.data; 36 | 37 | const result = await runApiCall(url, method, headers, body as JsonLikeObject, responseType); 38 | response.setSuccess(result); 39 | } catch (error: unknown) { 40 | const nodeError: GlobalError = new GlobalError((error as Error).message); 41 | nodeError.setCode(500); 42 | nodeError.setStack((error as Error).stack); 43 | nodeError.setName(this.name); 44 | response.setError(nodeError); 45 | } 46 | 47 | return response; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /nodes/web/api-call@1.0.0/inputSchema.ts: -------------------------------------------------------------------------------- 1 | export const inputSchema = { 2 | $schema: "http://json-schema.org/draft-07/schema#", 3 | title: "Generated schema for Root", 4 | type: "object", 5 | properties: { 6 | url: { 7 | type: "string", 8 | }, 9 | method: { 10 | type: "string", 11 | }, 12 | body: { 13 | type: "object", 14 | properties: {}, 15 | }, 16 | headers: { 17 | type: "object", 18 | properties: {}, 19 | }, 20 | responseType: { 21 | type: "string", 22 | }, 23 | }, 24 | required: ["url", "method"], 25 | }; 26 | -------------------------------------------------------------------------------- /nodes/web/api-call@1.0.0/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": ".ts,.js", 4 | "ignore": [], 5 | "exec": "pnpm --filter @nanoservice-ts/api-call run build" 6 | } 7 | -------------------------------------------------------------------------------- /nodes/web/api-call@1.0.0/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nanoservice-ts/api-call", 3 | "version": "0.1.29", 4 | "description": "Node module for making API calls", 5 | "engines": { 6 | "node": ">=18.0.0" 7 | }, 8 | "author": "Deskree Technologies Inc.", 9 | "license": "Apache-2.0", 10 | "main": "dist/index.js", 11 | "types": "dist/index.d.ts", 12 | "scripts": { 13 | "start:dev": "npx nodemon", 14 | "build": "rimraf ./dist && tsc", 15 | "build:dev": "tsc --watch", 16 | "test:dev": "vitest --watch", 17 | "test": "vitest run" 18 | }, 19 | "devDependencies": { 20 | "@types/lodash": "^4.14.196", 21 | "@types/node": "^22.13.4", 22 | "nodemon": "^3.1.9", 23 | "rimraf": "^6.0.1", 24 | "ts-node": "^10.9.1", 25 | "typescript": "^5.1.3", 26 | "vitest": "^3.1.4" 27 | }, 28 | "dependencies": { 29 | "@nanoservice-ts/runner": "workspace:*", 30 | "@nanoservice-ts/shared": "workspace:*", 31 | "lodash": "^4.17.21" 32 | }, 33 | "private": false 34 | } 35 | -------------------------------------------------------------------------------- /nodes/web/api-call@1.0.0/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "sourceMap": true, 7 | "outDir": "./dist", 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "strict": true, 11 | "noUnusedLocals": true, 12 | "noImplicitReturns": true, 13 | "skipLibCheck": true 14 | }, 15 | "compileOnSave": true 16 | } 17 | -------------------------------------------------------------------------------- /nodes/web/api-call@1.0.0/util.ts: -------------------------------------------------------------------------------- 1 | import type { JsonLikeObject } from "@nanoservice-ts/runner"; 2 | 3 | export const runApiCall = async ( 4 | url: string, 5 | method: string, 6 | headers: JsonLikeObject, 7 | body: JsonLikeObject, 8 | responseType: string, 9 | ): Promise => { 10 | const options: { 11 | method: string; 12 | headers: JsonLikeObject; 13 | redirect: "follow"; 14 | responseType: string; 15 | body: string | undefined; 16 | } = { 17 | method, 18 | headers, 19 | redirect: "follow", 20 | responseType, 21 | body: typeof body === "string" ? body : JSON.stringify(body), 22 | }; 23 | 24 | if (method === "GET") options.body = undefined; 25 | const response: Response = await fetch(url, options as RequestInit); 26 | 27 | if (response.status >= 400 && response.ok === false) { 28 | throw new Error(response.statusText); 29 | } 30 | 31 | let parsedResponse: string | JsonLikeObject; 32 | if (response.headers.get("content-type")?.includes("application/json")) { 33 | parsedResponse = await response.json(); 34 | } else { 35 | parsedResponse = await response.text(); 36 | } 37 | 38 | return parsedResponse; 39 | }; 40 | -------------------------------------------------------------------------------- /nodes/web/react@1.0.0/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/nodes/web/react@1.0.0/.DS_Store -------------------------------------------------------------------------------- /nodes/web/react@1.0.0/README.md: -------------------------------------------------------------------------------- 1 | # Node Documentation -------------------------------------------------------------------------------- /nodes/web/react@1.0.0/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": false 7 | } 8 | ], 9 | ["@babel/preset-react"], 10 | ["minify"] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /nodes/web/react@1.0.0/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-name", 3 | "version": "1.0.0", 4 | "description": "", 5 | "group": "API", 6 | "config": { 7 | "type": "object", 8 | "properties": { 9 | "inputs": { 10 | "type": "object", 11 | "properties": {}, 12 | "required": [] 13 | } 14 | }, 15 | "required": ["inputs"], 16 | "example": { 17 | "inputs": { 18 | "properties": { 19 | "url": "https://countriesnow.space/api/v0.1/countries/capital", 20 | "method": "POST", 21 | "headers": { 22 | "Content-Type": "application/json" 23 | }, 24 | "body": { 25 | "data": "Hello World" 26 | } 27 | } 28 | } 29 | } 30 | }, 31 | "input": { 32 | "anyOf": [ 33 | { 34 | "type": "object" 35 | }, 36 | { 37 | "type": "array" 38 | }, 39 | { 40 | "type": "string" 41 | } 42 | ], 43 | "description": "This node accepts an object as input from the previous node or request body" 44 | }, 45 | "output": { 46 | "type": "object", 47 | "description": "The response from the API call" 48 | }, 49 | "steps": { 50 | "type": "boolean", 51 | "default": false 52 | }, 53 | "functions": { 54 | "type": "array" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /nodes/web/react@1.0.0/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%-metas%> 8 | <%=title%> 9 | <%-styles%> 10 | 11 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | <%-scripts%> 21 | <%-react_app%> 22 | 23 | -------------------------------------------------------------------------------- /nodes/web/react@1.0.0/inputSchema.ts: -------------------------------------------------------------------------------- 1 | export const inputSchema = { 2 | $schema: "http://json-schema.org/draft-07/schema#", 3 | title: "Generated schema for Root", 4 | type: "object", 5 | properties: { 6 | title: { 7 | type: "string", 8 | }, 9 | index_html: { 10 | type: "string", 11 | }, 12 | scripts: { 13 | type: "string", 14 | }, 15 | react_app: { 16 | type: "string", 17 | }, 18 | styles: { 19 | type: "string", 20 | }, 21 | root_element: { 22 | type: "string", 23 | }, 24 | metas: { 25 | type: "string", 26 | }, 27 | }, 28 | required: ["react_app"], 29 | }; 30 | -------------------------------------------------------------------------------- /nodes/web/react@1.0.0/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src", "app"], 3 | "ext": "ts,jsx", 4 | "ignore": ["src/**/*.test.ts"], 5 | "exec": "tsc && npm run build:babel", 6 | "env": { 7 | "NODE_ENV": "local" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /nodes/web/react@1.0.0/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nanoservice-ts/react", 3 | "version": "0.0.17", 4 | "description": "", 5 | "engines": { 6 | "node": ">=18.0.0" 7 | }, 8 | "author": "Deskree Technologies Inc.", 9 | "license": "Apache-2.0", 10 | "main": "dist/index.js", 11 | "types": "dist/index.d.ts", 12 | "scripts": { 13 | "test:dev": "vitest", 14 | "test": "vitest run", 15 | "build:ts": "rm -rf dist && tsc", 16 | "build:dev": "nodemon", 17 | "build:babel": "rm -rf dist/app/index.merged.min.js && babel dist/app --out-file dist/app/index.merged.min.js", 18 | "build": "npm run build:ts && npm run build:babel && cp index.html dist/index.html" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "^22.13.4", 22 | "@types/ejs": "^3.1.5", 23 | "typescript": "^5.1.3", 24 | "vitest": "^3.1.4", 25 | "@babel/cli": "^7.0.0", 26 | "@babel/core": "^7.0.0", 27 | "@babel/preset-env": "^7.26.8", 28 | "@babel/preset-react": "^7.26.3", 29 | "babel-minify": "^0.5.2", 30 | "nodemon": "^3.1.9" 31 | }, 32 | "dependencies": { 33 | "@nanoservice-ts/shared": "workspace:*", 34 | "@nanoservice-ts/runner": "workspace:*", 35 | "@nanoservice-ts/helper": "workspace:*", 36 | "ejs": "^3.1.10" 37 | }, 38 | "private": false 39 | } 40 | -------------------------------------------------------------------------------- /nodes/web/react@1.0.0/test/helper.ts: -------------------------------------------------------------------------------- 1 | import type { ParamsDictionary } from "@nanoservice-ts/runner"; 2 | import type { Context } from "@nanoservice-ts/shared"; 3 | 4 | export default function ctx(): Context { 5 | const ctx: Context = { 6 | response: { 7 | data: { 8 | title: "Chat bot", 9 | }, 10 | error: null, 11 | }, 12 | request: { 13 | body: {}, 14 | }, 15 | config: {}, 16 | id: "", 17 | error: { 18 | message: "", 19 | code: undefined, 20 | json: undefined, 21 | stack: undefined, 22 | name: undefined, 23 | }, 24 | logger: { 25 | log: (message: string): void => { 26 | throw new Error("Function not implemented."); 27 | }, 28 | getLogs: (): string[] => { 29 | throw new Error("Function not implemented."); 30 | }, 31 | getLogsAsText: (): string => { 32 | throw new Error("Function not implemented."); 33 | }, 34 | getLogsAsBase64: (): string => { 35 | throw new Error("Function not implemented."); 36 | }, 37 | logLevel: (level: string, message: string): void => { 38 | throw new Error("Function not implemented."); 39 | }, 40 | error: (message: string, stack: string): void => { 41 | throw new Error("Function not implemented."); 42 | }, 43 | }, 44 | eventLogger: undefined, 45 | _PRIVATE_: undefined, 46 | }; 47 | 48 | ctx.config = { 49 | react: { 50 | inputs: { 51 | react_app: "./dist/app/index.merged.min.js", 52 | }, 53 | }, 54 | } as unknown as ParamsDictionary; 55 | 56 | return ctx; 57 | } 58 | -------------------------------------------------------------------------------- /nodes/web/react@1.0.0/test/index.test.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | import { beforeAll, expect, test } from "vitest"; 4 | import Node from "../index"; 5 | import ctx from "./helper"; 6 | 7 | let node: Node; 8 | let rootDir: string; 9 | 10 | beforeAll(() => { 11 | node = new Node(); 12 | node.name = "api-call"; 13 | rootDir = path.resolve(__dirname, "."); 14 | }); 15 | 16 | // Validate Hello World from Node 17 | test("Render index.html page", async () => { 18 | const response = await node.handle(ctx(), { react_app: "./dist/app/index.merged.min.js" }); 19 | const mockup_file = path.resolve(rootDir, "index.mockup.html"); 20 | const message: string = fs.readFileSync(mockup_file, "utf8"); 21 | 22 | expect(response.success).toEqual(true); 23 | expect(response.data).toEqual(message); 24 | }); 25 | -------------------------------------------------------------------------------- /nodes/web/react@1.0.0/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "allowJs": true, 5 | "module": "commonjs", 6 | "declaration": true, 7 | "sourceMap": true, 8 | "outDir": "./dist", 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "noUnusedLocals": true, 13 | "noImplicitReturns": true, 14 | "skipLibCheck": true 15 | }, 16 | "include": ["./*", "app/**/*", "index.html"], 17 | "exclude": ["node_modules", "dist"], 18 | "compileOnSave": true 19 | } 20 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/nx/schemas/nx-schema.json", 3 | "defaultBase": "main", 4 | "targetDefaults": { 5 | "build": { 6 | "dependsOn": ["^build"], 7 | "cache": true 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/cli/.gitignore: -------------------------------------------------------------------------------- 1 | projects -------------------------------------------------------------------------------- /packages/cli/src/commands/dev/index.ts: -------------------------------------------------------------------------------- 1 | import { type ChildProcess, spawn } from "node:child_process"; 2 | import type { OptionValues } from "commander"; 3 | 4 | const runningProcesses: ChildProcess[] = []; 5 | 6 | export async function devProject(opts: OptionValues) { 7 | const currentPath = process.cwd(); 8 | console.log("Starting the development server..."); 9 | console.log("Current path: ", currentPath); 10 | 11 | const processes = [ 12 | { cmd: "npx", args: ["nodemon@3.1.9"], name: "NodeJS Runner" }, 13 | { 14 | cmd: "npx", 15 | args: [ 16 | "nodemon@3.1.9", 17 | "--config", 18 | "./.nanoctl/runtimes/python3/nodemon.json", 19 | "--exec", 20 | "./.nanoctl/runtimes/python3/python3_runtime/bin/python3", 21 | "./.nanoctl/runtimes/python3/server.py", 22 | ], 23 | name: "Python3 Runner", 24 | }, 25 | ]; 26 | 27 | for (const { cmd, args, name } of processes) { 28 | const child = spawn(cmd, args, { stdio: "inherit" }); 29 | 30 | console.log(`✅ ${name} started (PID: ${child.pid})`); 31 | 32 | runningProcesses.push(child); 33 | 34 | child.on("exit", (code) => { 35 | console.log(`❌ ${name} exited with code ${code}`); 36 | }); 37 | 38 | child.on("error", (err) => { 39 | console.error(`❌ ${name} error: ${err}`); 40 | }); 41 | } 42 | 43 | // Capture CTRL+C to stop the processes 44 | process.on("SIGINT", () => { 45 | console.log("\n🛑 Stopping processes..."); 46 | for (const child of runningProcesses) { 47 | try { 48 | process.kill(child.pid as number, "SIGTERM"); 49 | console.log(`✅ Process ${child.pid} stopped.`); 50 | } catch (err: unknown) { 51 | console.error(`⚠️ Error stopping process ${child.pid}: ${(err as Error).message}`); 52 | } 53 | } 54 | process.exit(0); 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /packages/cli/src/commands/generate/NodeGenerator.ts: -------------------------------------------------------------------------------- 1 | import { createOpenAI } from "@ai-sdk/openai"; 2 | import { generateText } from "ai"; 3 | import createNodeSystemPrompt from "./prompts/create-node.system.js"; 4 | 5 | type NodeInformation = { 6 | nodeName: string; 7 | userPrompt: string; 8 | code: string; 9 | }; 10 | 11 | export default class NodeGenerator { 12 | async generateNode(nodeName: string, userPrompt: string, apiKey: string): Promise { 13 | const openai = createOpenAI({ 14 | compatibility: "strict", 15 | apiKey: apiKey, 16 | }); 17 | 18 | const { text } = await generateText({ 19 | model: openai("gpt-4o"), 20 | system: createNodeSystemPrompt.prompt, 21 | prompt: userPrompt, 22 | temperature: 0.2, 23 | }); 24 | 25 | return { 26 | nodeName, 27 | userPrompt, 28 | code: text, 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/cli/src/commands/generate/RegisterNode.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import { createOpenAI } from "@ai-sdk/openai"; 3 | import { generateText } from "ai"; 4 | import registerNodeSystemPrompt from "./prompts/register-node.system.js"; 5 | 6 | export default class RegisterNode { 7 | async generateNodesFile(nodeName: string, importPath: string, code: string, apiKey: string): Promise { 8 | const dirPath = process.cwd(); 9 | const nodesFile = `${dirPath}/src/Nodes.ts`; 10 | 11 | if (!fs.existsSync(nodesFile)) { 12 | throw new Error("The Nodes.ts file does not exist. Please ensure you are in the correct project directory."); 13 | } 14 | 15 | // Read the existing content of Nodes.ts 16 | const fileContent = fs.readFileSync(nodesFile, "utf8"); 17 | 18 | // Check if the node is already registered 19 | if (fileContent.includes(`"${nodeName}"`)) { 20 | console.log(`Node "${nodeName}" is already registered in Nodes.ts.`); 21 | } 22 | 23 | // Generate the import statement for the new node using openai 24 | const openai = createOpenAI({ 25 | compatibility: "strict", 26 | apiKey: apiKey, 27 | }); 28 | 29 | const { text } = await generateText({ 30 | model: openai("gpt-4o"), 31 | system: `${registerNodeSystemPrompt.prompt} \n${fileContent}`, 32 | prompt: `Node information: 33 | 34 | Name: ${nodeName} (This is the key in the nodes object) 35 | Import Path: ${importPath} 36 | Source Code: 37 | ${code} 38 | 39 | Take the class name from the source code and use it to register the node in Nodes.ts.`, 40 | temperature: 0.2, 41 | }); 42 | 43 | const cleaned = text.replace(/^```typescript\s*([\s\S]*?)\s*```$/gm, "$1"); 44 | 45 | // Rewrite the Nodes.ts file with the new node registration 46 | fs.writeFileSync(nodesFile, cleaned, "utf8"); 47 | 48 | return nodesFile; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/cli/src/commands/generate/prompts/register-node.system.ts: -------------------------------------------------------------------------------- 1 | const registerNodeSystemPrompt = { 2 | prompt: `You are a senior TypeScript developer and code editor assistant. Your task is to update a TypeScript file that exports a \`nodes\` registry object. This object contains imported nanoservice node classes (e.g., \`ApiCall\`, \`IfElse\`) as values, and their corresponding keys (string identifiers used in the system) as keys. 3 | 4 | You will receive: 5 | - The current full content of the TypeScript file. 6 | - The import path of the new node (e.g., "@nanoservice-ts/my-new-node" or "./nodes/remove-properties"). 7 | - The class name of the new node (e.g., "RemovePropertiesFromArray"). 8 | - The string **registry key** to register it under (e.g., "remove-properties"). 9 | 10 | Your task: 11 | 12 | 1. Add an \`import\` statement for the new node in the correct location, maintaining **alphabetical order** among existing regular imports. 13 | 2. The line \`import type { NodeBase } from "@nanoservice-ts/shared";\` must remain in place and not be reordered. 14 | 3. Add a new entry to the \`nodes\` object using the provided registry key only. Example: 15 | "remove-properties": new RemovePropertiesFromArray(), 16 | 4. **Do not modify any existing imports or registry entries.** Their keys must remain exactly as they appear. 17 | 5. **Do not change** the type of the \`nodes\` object or its declaration structure. Leave it as-is. 18 | 6. Keep the format, indentation, spacing, and structure exactly like the input. 19 | 7. If the registry key already exists in the object, do not add it again. 20 | 8. Your response must be a single full TypeScript file containing the updated code. No explanations or markdown formatting. 21 | 22 | Here is the current content of the TypeScript file: 23 | `, 24 | }; 25 | 26 | export default registerNodeSystemPrompt; 27 | -------------------------------------------------------------------------------- /packages/cli/src/commands/logout/index.ts: -------------------------------------------------------------------------------- 1 | import * as p from "@clack/prompts"; 2 | import { type OptionValues, program, trackCommandExecution } from "../../services/commander.js"; 3 | 4 | import { tokenManager } from "../../services/local-token-manager.js"; 5 | 6 | export async function logout(opts: OptionValues) { 7 | tokenManager.clearToken(); 8 | p.log.success("Logged out successfully."); 9 | p.log.info("You can log in again using: nanoctl login"); 10 | } 11 | 12 | // Logout command 13 | program 14 | .command("logout") 15 | .description("Logout from Nanoservices") 16 | .action(async (options: OptionValues) => { 17 | await trackCommandExecution({ 18 | command: "logout", 19 | args: options, 20 | execution: async () => { 21 | await logout(options); 22 | }, 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/cli/src/commands/monitor/index.ts: -------------------------------------------------------------------------------- 1 | import { type OptionValues, program, trackCommandExecution } from "../../services/commander.js"; 2 | import { runMonitor } from "./monitor-component.js"; 3 | import startWebMonitorUI from "./static-web-server.js"; 4 | 5 | // Logout command 6 | program 7 | .command("monitor") 8 | .description("Monitor the performance of your workflows") 9 | .option("--web", "Open the metrics dashboard in your browser") 10 | .option("--host ", "Remote prometheus host") 11 | .option("--token ", "Remote prometheus token") 12 | .action(async (options: OptionValues) => { 13 | await trackCommandExecution({ 14 | command: "monitor", 15 | args: options, 16 | execution: async () => { 17 | if (options.web) { 18 | await startWebMonitorUI(options.host || "http://localhost:9090", options.token); 19 | } else { 20 | runMonitor(options.host, options.token); 21 | } 22 | }, 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/cli/src/services/commander.ts: -------------------------------------------------------------------------------- 1 | import os from "node:os"; 2 | import { Command, type OptionValues } from "commander"; 3 | import { PosthogAnalytics } from "./posthog.js"; 4 | import { getPackageVersion } from "./utils.js"; 5 | 6 | const version = await getPackageVersion(); 7 | const program = new Command(); 8 | 9 | const HOME_DIR = `${os.homedir()}/.nanoctl`; 10 | const cliConfigPath = `${HOME_DIR}/nanoctl.json`; 11 | 12 | const analytics = new PosthogAnalytics({ 13 | version: version, 14 | cliConfigPath: cliConfigPath, 15 | }); 16 | 17 | type TrackCommandExecutionParams = { 18 | command: string; 19 | args: OptionValues; 20 | execution: () => Promise; 21 | }; 22 | 23 | const trackCommandExecution = async ({ command, args, execution }: TrackCommandExecutionParams) => { 24 | await analytics.trackCommandExecution({ 25 | command: command, 26 | args: args, 27 | execution, 28 | }); 29 | }; 30 | 31 | export { program, trackCommandExecution, type OptionValues, type TrackCommandExecutionParams }; 32 | -------------------------------------------------------------------------------- /packages/cli/src/services/constants.ts: -------------------------------------------------------------------------------- 1 | export const NANOSERVICE_URL = 2 | process.env.NANOSERVICE_DEV === "true" 3 | ? "https://runner-dev.dac-us-east-1.deskree.com/public/deployment" 4 | : "https://runner.dac-us-east-1.deskree.com/public/deployment"; 5 | -------------------------------------------------------------------------------- /packages/cli/src/services/utils.ts: -------------------------------------------------------------------------------- 1 | import path, { dirname } from "node:path"; 2 | import { fileURLToPath } from "node:url"; 3 | import fs from "fs-extra"; 4 | import type { PackageJson } from "type-fest"; 5 | 6 | export async function getPackageVersion() { 7 | // @ts-ignore 8 | const __filename = fileURLToPath(import.meta.url); 9 | 10 | const __dirname = dirname(__filename); 11 | const pkgJsonPath = path.join(__dirname, "..", "..", "package.json"); 12 | 13 | const content = (await fs.readJSON(pkgJsonPath)) as PackageJson; 14 | return content?.version as string; 15 | } 16 | -------------------------------------------------------------------------------- /packages/cli/tests/commands/create/node.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { createNode } from "../../../src/commands/create/node"; 3 | 4 | test("create node", async () => { 5 | expect(async () => await createNode({ name: "default-node" })).not.toThrow(); 6 | }); 7 | -------------------------------------------------------------------------------- /packages/cli/tests/commands/create/project.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { createProject } from "../../../src/commands/create/project"; 3 | 4 | test("create project", async () => { 5 | expect(async () => await createProject({ name: "default-node" })).not.toThrow(); 6 | }); 7 | 8 | test("create path", async () => { 9 | const env = "WORKFLOWS_PATH=PROJECT_PATH/workflows,NODES_PATH=PROJECT_PATH/src/nodes"; 10 | const path = "/home/ubuntu"; 11 | const result = env.replaceAll("PROJECT_PATH", path); 12 | 13 | expect(result).toBe("WORKFLOWS_PATH=/home/ubuntu/workflows,NODES_PATH=/home/ubuntu/src/nodes"); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/cli/tests/commands/login/login.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { login } from "../../../src/commands/login"; 3 | 4 | test("login", async () => { 5 | expect(async () => await login({ token: "test" })).not.toThrow(); 6 | }); 7 | -------------------------------------------------------------------------------- /packages/cli/tests/commands/logout/logout.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { logout } from "../../../src/commands/logout"; 3 | 4 | test("logout", async () => { 5 | expect(async () => await logout({ token: "test" })).not.toThrow(); 6 | }); 7 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "es2022", 5 | "rootDir": "./src", 6 | "moduleResolution": "bundler", 7 | "resolveJsonModule": true, 8 | "declaration": true, 9 | "noEmit": false, 10 | "outDir": "./dist", 11 | "removeComments": true, 12 | "esModuleInterop": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "strict": true, 15 | "noUnusedLocals": true, 16 | "noImplicitReturns": true, 17 | "skipLibCheck": true, 18 | "jsx": "react-jsx", 19 | "jsxImportSource": "react", 20 | "types": ["node"] 21 | }, 22 | "include": ["./src/**/*.ts", "./src/**/*.tsx", "./package.json"] 23 | } 24 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "core/*" 3 | - "nodes/**/**" 4 | - "triggers/*" 5 | - "templates/**/**" 6 | - "packages/*" -------------------------------------------------------------------------------- /runtimes/proto/node.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package nanoservice.workflow.v1; 4 | 5 | message NodeRequest { 6 | string Name = 1; 7 | string Message = 2; 8 | string Encoding = 3; 9 | string Type = 4; 10 | } 11 | 12 | message NodeResponse { 13 | string Message = 1; 14 | string Encoding = 2; 15 | string Type = 3; 16 | } 17 | 18 | enum MessageEncoding { 19 | BASE64 = 0; 20 | STRING = 1; 21 | } 22 | 23 | enum MessageType { 24 | TEXT = 0; 25 | JSON = 1; 26 | XML = 2; 27 | HTML = 3; 28 | BINARY = 4; 29 | } 30 | 31 | service NodeService { 32 | rpc ExecuteNode (NodeRequest) returns (NodeResponse) {} 33 | } -------------------------------------------------------------------------------- /runtimes/python3/Makefile: -------------------------------------------------------------------------------- 1 | create-env: 2 | python3 python3 -m venv python3_runtime 3 | open-env: 4 | source $(path)/python3_runtime/bin/activate 5 | install: 6 | pip3 install -r requirements.txt 7 | test: 8 | python3 -m unittest 9 | requirements: 10 | pip3 freeze > requirements.txt 11 | generate-proto: 12 | python -m grpc_tools.protoc -I. --python_out=./gen/. --grpc_python_out=./gen/. --proto_path=../proto node.proto -------------------------------------------------------------------------------- /runtimes/python3/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/runtimes/python3/__init__.py -------------------------------------------------------------------------------- /runtimes/python3/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/runtimes/python3/core/__init__.py -------------------------------------------------------------------------------- /runtimes/python3/core/types/config.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, Optional 2 | 3 | class ConfigContext: 4 | def __init__(self): 5 | self.nodes: Optional[Dict[str, Dict[str, Any]]] = None 6 | -------------------------------------------------------------------------------- /runtimes/python3/core/types/context.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | from core.types.response import ResponseContext 3 | from core.types.error import ErrorContext 4 | from core.types.config import ConfigContext 5 | 6 | class Context: 7 | def __init__(self): 8 | self.id: str = "" 9 | self.workflow_name: str = "" 10 | self.workflow_path: str = "" 11 | self.request: Dict[str, Any] = {} 12 | self.response: ResponseContext = {} 13 | self.error: ErrorContext = {} 14 | self.logger: Optional[Any] = None 15 | self.config: Dict[str, Any] = {} 16 | self.func: Dict[str, Any] = {} 17 | self.vars: Dict[str, Any] = {} 18 | self.env: Dict[str, Any] = {} -------------------------------------------------------------------------------- /runtimes/python3/core/types/error.py: -------------------------------------------------------------------------------- 1 | from typing import List, Union, Optional, Dict 2 | 3 | class ErrorContext: 4 | def __init__(self): 5 | self.message: Union[List[str], str] = "" 6 | self.code: int = 0 7 | self.json: Optional[Dict[str, str]] = None 8 | self.stack: Optional[str] = None 9 | self.name: Optional[str] = None -------------------------------------------------------------------------------- /runtimes/python3/core/types/global_error.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Dict, Any 2 | 3 | class GlobalError(Exception): 4 | def __init__(self, msg: Optional[str] = None): 5 | Exception.__init__(self) 6 | self.context: Dict[str, Any] = {'message': msg} 7 | self.message: str = msg 8 | self.code = 500 9 | 10 | def setCode(self, code: Optional[int] = None) -> None: 11 | self.code = code 12 | self.context['code'] = code 13 | 14 | def setJson(self, json: Optional[Dict[str, Any]] = None) -> None: 15 | self.context['json'] = json 16 | 17 | def setStack(self, stack: Optional[str] = None) -> None: 18 | self.context['stack'] = stack 19 | 20 | def setName(self, name: Optional[str] = None) -> None: 21 | self.context['name'] = name 22 | 23 | def hasJson(self) -> bool: 24 | return 'json' in self.context 25 | 26 | def __str__(self) -> str: 27 | if 'json' in self.context: 28 | return str(self.context['json']) 29 | return self.context['message'] or '' 30 | 31 | def to_dict(self): 32 | return self.context 33 | -------------------------------------------------------------------------------- /runtimes/python3/core/types/logger.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | from typing import List 3 | 4 | class LoggerContext(ABC): 5 | def log(self, message: str) -> None: 6 | pass 7 | 8 | def getLogs(self) -> List[str]: 9 | pass 10 | 11 | def getLogsAsText(self) -> str: 12 | pass 13 | 14 | def getLogsAsBase64(self) -> str: 15 | pass 16 | 17 | def logLevel(self, level: str, message: str) -> None: 18 | pass 19 | 20 | def error(self, message: str, stack: str) -> None: 21 | pass -------------------------------------------------------------------------------- /runtimes/python3/core/types/nanoservice_response.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional, Any 2 | from core.types.global_error import GlobalError 3 | from core.node_base import NodeBase 4 | from core.types.response import ResponseContext 5 | 6 | class NanoServiceResponse(ResponseContext): 7 | def __init__(self): 8 | self.steps: List[NodeBase] = [] 9 | self.data: Any = {} 10 | self.error: Optional[GlobalError] = None 11 | self.success: Optional[bool] = True 12 | self.contentType: Optional[str] = "application/json" 13 | 14 | def setError(self, error: GlobalError) -> None: 15 | self.error = error 16 | self.success = False 17 | self.data = {} 18 | 19 | def setSuccess(self, data: Any) -> None: 20 | self.data = data 21 | self.error = None 22 | self.success = True 23 | -------------------------------------------------------------------------------- /runtimes/python3/core/types/response.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Optional 2 | from core.types.global_error import GlobalError 3 | 4 | class ResponseContext: 5 | def __init__(self, data: Any = {}, error: Optional[GlobalError] = None, success: bool = False, contentType: str = "application/json"): 6 | self.data: Any = data 7 | self.error: Optional[GlobalError] = error 8 | self.success: bool = success 9 | self.contentType: str = contentType 10 | 11 | def to_dict(self): 12 | return { 13 | "data": self.data, 14 | "error": self.error.to_dict() if self.error else None, 15 | "success": self.success, 16 | "contentType": self.contentType 17 | } -------------------------------------------------------------------------------- /runtimes/python3/core/types/step.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/runtimes/python3/core/types/step.py -------------------------------------------------------------------------------- /runtimes/python3/gen/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/runtimes/python3/gen/__init__.py -------------------------------------------------------------------------------- /runtimes/python3/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["*"], 3 | "ext": "py", 4 | "ignore": ["node_modules", ".git"], 5 | "exec": "python3 server.py", 6 | "env": { 7 | "NODE_ENV": "local" 8 | }, 9 | "ignoreRoot": [".git"] 10 | } 11 | -------------------------------------------------------------------------------- /runtimes/python3/nodes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/runtimes/python3/nodes/__init__.py -------------------------------------------------------------------------------- /runtimes/python3/nodes/embed/README.md: -------------------------------------------------------------------------------- 1 | ## UNIT TEST 2 | 3 | ```sh 4 | PYTHONWARNINGS="ignore::ResourceWarning" python3 -m unittest nodes.embed.test_node 5 | ``` -------------------------------------------------------------------------------- /runtimes/python3/nodes/embed/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/runtimes/python3/nodes/embed/__init__.py -------------------------------------------------------------------------------- /runtimes/python3/nodes/embed/test_node.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import MagicMock 3 | from PIL import Image # type: ignore 4 | from io import BytesIO 5 | import base64 6 | 7 | from nodes.embed.node import EmbeddingClip 8 | from core.types.context import Context 9 | 10 | class TestEmbeddingClip(unittest.IsolatedAsyncioTestCase): 11 | 12 | def setUp(self): 13 | self.node = EmbeddingClip() 14 | 15 | self.node.embed_text = MagicMock(return_value=[0.1, 0.2, 0.3]) 16 | self.node.embed_image = MagicMock(return_value=[0.4, 0.5, 0.6]) 17 | 18 | def create_base64_image(self): 19 | image = Image.new("RGB", (50, 50), color="blue") 20 | buffer = BytesIO() 21 | image.save(buffer, format="JPEG") 22 | return "data:image/jpeg;base64," + base64.b64encode(buffer.getvalue()).decode() 23 | 24 | async def test_handle_success(self): 25 | ctx = Context() 26 | inputs = { 27 | "description": "Un paisaje azul", 28 | "image_base64": self.create_base64_image() 29 | } 30 | 31 | response = await self.node.handle(ctx, inputs) 32 | 33 | self.assertTrue(response.success) 34 | self.assertIn("text_vector", response.data) 35 | self.assertIn("image_vector", response.data) 36 | self.assertEqual(response.data["text_vector"], [0.1, 0.2, 0.3]) 37 | self.assertEqual(response.data["image_vector"], [0.4, 0.5, 0.6]) 38 | 39 | if __name__ == "__main__": 40 | unittest.main() 41 | -------------------------------------------------------------------------------- /runtimes/python3/nodes/generate_pdf/reports/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/runtimes/python3/nodes/generate_pdf/reports/.gitkeep -------------------------------------------------------------------------------- /runtimes/python3/nodes/milvus/insert/test_node.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import patch, MagicMock 3 | from core.types.context import Context 4 | 5 | class TestStoreEmbeddings(unittest.IsolatedAsyncioTestCase): 6 | 7 | @patch("nodes.milvus.insert.node.list_collections", return_value=["multimodal_index"]) 8 | @patch("nodes.milvus.insert.node.Collection") 9 | @patch("nodes.milvus.insert.node.connections.connect") 10 | async def test_handle_success(self, mock_connect, mock_collection_cls, mock_list_collections): 11 | mock_collection = MagicMock() 12 | mock_collection_cls.return_value = mock_collection 13 | 14 | from nodes.milvus.insert.node import StoreInMilvus 15 | node = StoreInMilvus() 16 | 17 | inputs = { 18 | "description": "A photo of mountains", 19 | "image_url": "https://miweb.com/img.jpg", 20 | "text_vector": [0.1] * 512, 21 | "image_vector": [0.2] * 512 22 | } 23 | 24 | ctx = Context() 25 | response = await node.handle(ctx, inputs) 26 | 27 | self.assertTrue(response.success) 28 | self.assertEqual(response.data["inserted"], True) 29 | mock_collection.insert.assert_called_once() 30 | 31 | if __name__ == "__main__": 32 | unittest.main() 33 | -------------------------------------------------------------------------------- /runtimes/python3/nodes/nodes.py: -------------------------------------------------------------------------------- 1 | from nodes.api_call.node import ApiCall 2 | from nodes.sentiment.node import Sentiment 3 | from nodes.generate_pdf.node import GeneratePDF 4 | # from nodes.embed.node import EmbeddingClip 5 | # from nodes.milvus.insert.node import StoreInMilvus 6 | # from nodes.milvus.query.node import SearchInMilvus 7 | # from nodes.image_description.node import GenerateCaption 8 | 9 | nodes = { 10 | "api_call": ApiCall(), 11 | "generate-sentiment": Sentiment(), 12 | "generate-pdf": GeneratePDF() 13 | } 14 | 15 | def get_nodes(): 16 | return nodes 17 | -------------------------------------------------------------------------------- /runtimes/python3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "python3", 3 | "version": "1.0.0", 4 | "description": "python3 node runtime", 5 | "main": "server.py", 6 | "directories": { 7 | "test": "tests" 8 | }, 9 | "scripts": { 10 | "dev": "nodemon" 11 | }, 12 | "keywords": ["nanoservice", "node"], 13 | "author": "Marco A. Castillo Della Sera", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "nodemon": "^3.1.9" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /runtimes/python3/requirements.txt: -------------------------------------------------------------------------------- 1 | aiodns==3.2.0 2 | aiohappyeyeballs==2.6.1 3 | aiohttp==3.11.13 4 | aiosignal==1.3.2 5 | attrs==25.2.0 6 | certifi==2025.4.26 7 | cffi==1.17.1 8 | charset-normalizer==3.4.1 9 | click==8.1.8 10 | clip @ git+https://github.com/openai/CLIP.git@dcba3cb2e2827b402d2701e7e1c7d9fed8a20ef1 11 | filelock==3.18.0 12 | fpdf==1.7.2 13 | frozenlist==1.5.0 14 | fsspec==2025.3.2 15 | ftfy==6.3.1 16 | grpcio==1.67.1 17 | grpcio-tools==1.67.1 18 | huggingface-hub==0.30.2 19 | idna==3.10 20 | Jinja2==3.1.6 21 | joblib==1.4.2 22 | jsonschema==4.23.0 23 | jsonschema-specifications==2024.10.1 24 | MarkupSafe==3.0.2 25 | milvus-lite==2.4.12 26 | mpmath==1.3.0 27 | multidict==6.1.0 28 | networkx==3.4.2 29 | nltk==3.9.1 30 | numpy==2.2.5 31 | packaging==25.0 32 | pandas==2.2.3 33 | pillow==11.2.1 34 | propcache==0.3.0 35 | protobuf==5.29.3 36 | pycares==4.5.0 37 | pycparser==2.22 38 | pymilvus==2.5.7 39 | python-dateutil==2.9.0.post0 40 | python-dotenv==1.1.0 41 | pytz==2025.2 42 | PyYAML==6.0.2 43 | referencing==0.36.2 44 | regex==2024.11.6 45 | requests==2.32.3 46 | rpds-py==0.23.1 47 | safetensors==0.5.3 48 | setuptools==76.0.0 49 | six==1.17.0 50 | sympy==1.13.3 51 | textblob==0.19.0 52 | tokenizers==0.15.2 53 | torch==2.7.0 54 | torchvision==0.22.0 55 | tqdm==4.67.1 56 | transformers==4.38.0 57 | typing_extensions==4.13.2 58 | tzdata==2025.2 59 | ujson==5.10.0 60 | urllib3==2.4.0 61 | wcwidth==0.2.13 62 | yarl==1.18.3 63 | -------------------------------------------------------------------------------- /runtimes/python3/runner.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict 2 | from nodes.nodes import get_nodes 3 | from core.node_base import NodeBase 4 | from core.types.context import Context 5 | 6 | class Runner: 7 | def __init__(self, node_name: str, ctx: Dict[str, Any]): 8 | self.nodes = get_nodes() 9 | self.ctx = self.create_context(ctx) 10 | self.node_name = node_name 11 | 12 | async def run(self): 13 | node: NodeBase = self.node_resolver(self.node_name, self.ctx.config) 14 | model = await node.process(self.ctx) 15 | return model.data 16 | 17 | def node_resolver(self, node_name: str, config: Dict[str, Any]) -> NodeBase: 18 | node: NodeBase = self.nodes[node_name] 19 | node.node = config.get('node') 20 | node.name = config.get('name') 21 | node.active = config.get('active', True) 22 | node.stop = config.get('stop', False) 23 | node.set_var = config.get('set_var', False) 24 | node.originalConfig = config 25 | return node 26 | 27 | def create_context(self, ctx: Dict[str, Any]) -> Context: 28 | context = Context() 29 | context.id = ctx.get('id', '') 30 | context.workflow_name = ctx.get('workflow_name', '') 31 | context.workflow_path = ctx.get('workflow_path', '') 32 | context.request = ctx.get('request', {}) 33 | context.response = ctx.get('response', {}) 34 | context.error = ctx.get('error', None) 35 | context.logger = ctx.get('logger', None) 36 | context.config = ctx.get('config', {}) 37 | context.func = ctx.get('func', None) 38 | context.vars = ctx.get('vars', {}) 39 | context.env = ctx.get('env', {}) 40 | 41 | return context -------------------------------------------------------------------------------- /runtimes/python3/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/runtimes/python3/tests/__init__.py -------------------------------------------------------------------------------- /runtimes/python3/tests/test_mapper.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from core.util.mapper import Mapper 3 | 4 | class TestMapper(unittest.TestCase): 5 | def setUp(self): 6 | self.mapper = Mapper() 7 | 8 | def test_replace_object_strings(self): 9 | obj = { 10 | "key1": "value1", 11 | "key2": "${replace_me}", 12 | "nested": { 13 | "key3": "nested_${replace_me}" 14 | } 15 | } 16 | ctx = {} 17 | data = {"replace_me": "replaced_value"} 18 | self.mapper.replace_object_strings(obj, ctx, data) 19 | self.assertEqual(obj["key2"], "replaced_value") 20 | self.assertEqual(obj["nested"]["key3"], "nested_replaced_value") 21 | 22 | def test_replace_string(self): 23 | str_data = "This is a ${replace_me} string" 24 | ctx = {} 25 | data = {"replace_me": "replaced_value"} 26 | result = self.mapper.replace_string(str_data, ctx, data) 27 | self.assertEqual(result, "This is a replaced_value string") 28 | 29 | def test_run_js(self): 30 | str_ = "data['key'] + 1" 31 | ctx = {} 32 | data = {"key": 1} 33 | result = self.mapper.run_js(str_, ctx, data) 34 | self.assertEqual(result, 2) 35 | 36 | def test_js_mapper(self): 37 | str_ = "js/data['key'] + 1" 38 | ctx = {} 39 | data = {"key": 1} 40 | result = self.mapper.js_mapper(str_, ctx, data) 41 | self.assertEqual(result, 2) 42 | 43 | def test_js_mapper_no_js_prefix(self): 44 | str_ = "no_js_prefix" 45 | ctx = {} 46 | data = {} 47 | result = self.mapper.js_mapper(str_, ctx, data) 48 | self.assertEqual(result, "no_js_prefix") 49 | 50 | if __name__ == '__main__': 51 | unittest.main() -------------------------------------------------------------------------------- /runtimes/python3/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/runtimes/python3/util/__init__.py -------------------------------------------------------------------------------- /templates/node-ui/README.md: -------------------------------------------------------------------------------- 1 | # Node Documentation -------------------------------------------------------------------------------- /templates/node-ui/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": false 7 | } 8 | ], 9 | ["@babel/preset-react"], 10 | ["minify"] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /templates/node-ui/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-name", 3 | "version": "1.0.0", 4 | "description": "", 5 | "group": "API", 6 | "config": { 7 | "type": "object", 8 | "properties": { 9 | "inputs": { 10 | "type": "object", 11 | "properties": {}, 12 | "required": [] 13 | } 14 | }, 15 | "required": ["inputs"], 16 | "example": { 17 | "inputs": { 18 | "properties": { 19 | "url": "https://countriesnow.space/api/v0.1/countries/capital", 20 | "method": "POST", 21 | "headers": { 22 | "Content-Type": "application/json" 23 | }, 24 | "body": { 25 | "data": "Hello World" 26 | } 27 | } 28 | } 29 | } 30 | }, 31 | "input": { 32 | "anyOf": [ 33 | { 34 | "type": "object" 35 | }, 36 | { 37 | "type": "array" 38 | }, 39 | { 40 | "type": "string" 41 | } 42 | ], 43 | "description": "This node accepts an object as input from the previous node or request body" 44 | }, 45 | "output": { 46 | "type": "object", 47 | "description": "The response from the API call" 48 | }, 49 | "steps": { 50 | "type": "boolean", 51 | "default": false 52 | }, 53 | "functions": { 54 | "type": "array" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /templates/node-ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%-metas%> 8 | <%=title%> 9 | <%-styles%> 10 | 11 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | <%-scripts%> 21 | <%-react_app%> 22 | 23 | -------------------------------------------------------------------------------- /templates/node-ui/inputSchema.ts: -------------------------------------------------------------------------------- 1 | export const inputSchema = { 2 | $schema: "http://json-schema.org/draft-07/schema#", 3 | title: "Generated schema for Root", 4 | type: "object", 5 | properties: { 6 | title: { 7 | type: "string", 8 | }, 9 | index_html: { 10 | type: "string", 11 | }, 12 | scripts: { 13 | type: "string", 14 | }, 15 | react_app: { 16 | type: "string", 17 | }, 18 | styles: { 19 | type: "string", 20 | }, 21 | root_element: { 22 | type: "string", 23 | }, 24 | metas: { 25 | type: "string", 26 | }, 27 | }, 28 | required: ["react_app"], 29 | }; 30 | -------------------------------------------------------------------------------- /templates/node-ui/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src", "app"], 3 | "ext": "ts,jsx", 4 | "ignore": ["src/**/*.test.ts"], 5 | "exec": "tsc && npm run build:babel", 6 | "env": { 7 | "NODE_ENV": "local" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /templates/node-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-ui-name", 3 | "version": "0.0.3", 4 | "description": "", 5 | "engines": { 6 | "node": ">=18.0.0" 7 | }, 8 | "author": "Deskree Technologies Inc.", 9 | "license": "Apache-2.0", 10 | "main": "dist/index.js", 11 | "types": "dist/index.d.ts", 12 | "scripts": { 13 | "test:dev": "vitest", 14 | "test": "vitest run", 15 | "build:ts": "rm -rf dist && tsc", 16 | "build:dev": "nodemon", 17 | "build:babel": "rm -rf dist/app/index.merged.min.js && babel dist/app --out-file dist/app/index.merged.min.js", 18 | "build": "npm run build:ts && npm run build:babel && cp index.html dist/index.html" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "^22.13.4", 22 | "@types/ejs": "^3.1.5", 23 | "typescript": "^5.1.3", 24 | "vitest": "^3.1.4", 25 | "@babel/cli": "^7.0.0", 26 | "@babel/core": "^7.0.0", 27 | "@babel/preset-env": "^7.26.8", 28 | "@babel/preset-react": "^7.26.3", 29 | "babel-minify": "^0.5.2", 30 | "nodemon": "^3.1.9" 31 | }, 32 | "dependencies": { 33 | "@nanoservice-ts/shared": "^0.0.9", 34 | "@nanoservice-ts/runner": "^0.1.21", 35 | "@nanoservice-ts/helper": "^0.1.4", 36 | "ejs": "^3.1.10" 37 | }, 38 | "private": true 39 | } 40 | -------------------------------------------------------------------------------- /templates/node-ui/test/helper.ts: -------------------------------------------------------------------------------- 1 | import type { ParamsDictionary } from "@nanoservice-ts/runner"; 2 | import type { Context } from "@nanoservice-ts/shared"; 3 | 4 | export default function ctx(): Context { 5 | const ctx: Context = { 6 | response: { 7 | data: { 8 | title: "Chat bot", 9 | }, 10 | error: null, 11 | }, 12 | request: { 13 | body: {}, 14 | }, 15 | config: {}, 16 | id: "", 17 | error: { 18 | message: "", 19 | code: undefined, 20 | json: undefined, 21 | stack: undefined, 22 | name: undefined, 23 | }, 24 | logger: { 25 | log: (message: string): void => { 26 | throw new Error("Function not implemented."); 27 | }, 28 | getLogs: (): string[] => { 29 | throw new Error("Function not implemented."); 30 | }, 31 | getLogsAsText: (): string => { 32 | throw new Error("Function not implemented."); 33 | }, 34 | getLogsAsBase64: (): string => { 35 | throw new Error("Function not implemented."); 36 | }, 37 | logLevel: (level: string, message: string): void => { 38 | throw new Error("Function not implemented."); 39 | }, 40 | error: (message: string, stack: string): void => { 41 | throw new Error("Function not implemented."); 42 | }, 43 | }, 44 | eventLogger: undefined, 45 | _PRIVATE_: undefined, 46 | }; 47 | 48 | ctx.config = { 49 | react: { 50 | inputs: { 51 | react_app: "./dist/app/index.merged.min.js", 52 | }, 53 | }, 54 | } as unknown as ParamsDictionary; 55 | 56 | return ctx; 57 | } 58 | -------------------------------------------------------------------------------- /templates/node-ui/test/index.test.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | import { beforeAll, expect, test } from "vitest"; 4 | import Node from "../index"; 5 | import ctx from "./helper"; 6 | 7 | let node: Node; 8 | let rootDir: string; 9 | 10 | beforeAll(() => { 11 | node = new Node(); 12 | node.name = "api-call"; 13 | rootDir = path.resolve(__dirname, "."); 14 | }); 15 | 16 | // Validate Hello World from Node 17 | test("Render index.html page", async () => { 18 | const response = await node.handle(ctx(), { react_app: "./dist/app/index.merged.min.js" }); 19 | const mockup_file = path.resolve(rootDir, "index.mockup.html"); 20 | const message: string = fs.readFileSync(mockup_file, "utf8"); 21 | 22 | expect(response.success).toEqual(true); 23 | expect(response.data).toEqual(message); 24 | }); 25 | -------------------------------------------------------------------------------- /templates/node-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "allowJs": true, 5 | "module": "commonjs", 6 | "declaration": true, 7 | "sourceMap": true, 8 | "outDir": "./dist", 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "noUnusedLocals": true, 13 | "noImplicitReturns": true, 14 | "skipLibCheck": true 15 | }, 16 | "include": ["./*", "app/**/*", "index.html"], 17 | "exclude": ["node_modules", "dist"], 18 | "compileOnSave": true 19 | } 20 | -------------------------------------------------------------------------------- /templates/node/README.md: -------------------------------------------------------------------------------- 1 | # Node Documentation -------------------------------------------------------------------------------- /templates/node/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-name", 3 | "version": "1.0.0", 4 | "description": "", 5 | "group": "API", 6 | "config": { 7 | "type": "object", 8 | "properties": { 9 | "inputs": { 10 | "type": "object", 11 | "properties": {}, 12 | "required": [] 13 | } 14 | }, 15 | "required": ["inputs"], 16 | "example": { 17 | "inputs": { 18 | "properties": { 19 | "url": "https://countriesnow.space/api/v0.1/countries/capital", 20 | "method": "POST", 21 | "headers": { 22 | "Content-Type": "application/json" 23 | }, 24 | "body": { 25 | "data": "Hello World" 26 | } 27 | } 28 | } 29 | } 30 | }, 31 | "input": { 32 | "anyOf": [ 33 | { 34 | "type": "object" 35 | }, 36 | { 37 | "type": "array" 38 | }, 39 | { 40 | "type": "string" 41 | } 42 | ], 43 | "description": "This node accepts an object as input from the previous node or request body" 44 | }, 45 | "output": { 46 | "type": "object", 47 | "description": "The response from the API call" 48 | }, 49 | "steps": { 50 | "type": "boolean", 51 | "default": false 52 | }, 53 | "functions": { 54 | "type": "array" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /templates/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-name", 3 | "version": "0.0.17", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "test:dev": "vitest", 9 | "test": "vitest run", 10 | "build": "rm -rf dist && tsc", 11 | "build:dev": "tsc --watch" 12 | }, 13 | "author": "Deskree Technologies Inc.", 14 | "license": "Apache-2.0", 15 | "devDependencies": { 16 | "@types/node": "^22.13.4", 17 | "typescript": "^5.1.3", 18 | "vitest": "^3.1.4" 19 | }, 20 | "dependencies": { 21 | "@nanoservice-ts/shared": "^0.0.9", 22 | "@nanoservice-ts/runner": "^0.1.21", 23 | "@nanoservice-ts/helper": "^0.1.4" 24 | }, 25 | "private": true 26 | } 27 | -------------------------------------------------------------------------------- /templates/node/test/helper.ts: -------------------------------------------------------------------------------- 1 | import type { ParamsDictionary } from "@nanoservice-ts/runner"; 2 | import type { Context } from "@nanoservice-ts/shared"; 3 | 4 | export default function ctx(): Context { 5 | const ctx: Context = { 6 | response: { 7 | data: null, 8 | error: null, 9 | }, 10 | request: { 11 | body: {}, 12 | }, 13 | config: {}, 14 | id: "", 15 | error: { 16 | message: "", 17 | code: undefined, 18 | json: undefined, 19 | stack: undefined, 20 | name: undefined, 21 | }, 22 | logger: { 23 | log: (message: string): void => { 24 | throw new Error("Function not implemented."); 25 | }, 26 | getLogs: (): string[] => { 27 | throw new Error("Function not implemented."); 28 | }, 29 | getLogsAsText: (): string => { 30 | throw new Error("Function not implemented."); 31 | }, 32 | getLogsAsBase64: (): string => { 33 | throw new Error("Function not implemented."); 34 | }, 35 | logLevel: (level: string, message: string): void => { 36 | throw new Error("Function not implemented."); 37 | }, 38 | error: (message: string, stack: string): void => { 39 | throw new Error("Function not implemented."); 40 | }, 41 | }, 42 | eventLogger: undefined, 43 | _PRIVATE_: undefined, 44 | }; 45 | 46 | ctx.config = { 47 | "api-call": { 48 | inputs: { 49 | url: "https://jsonplaceholder.typicode.com/todos/1", 50 | method: "GET", 51 | }, 52 | }, 53 | } as unknown as ParamsDictionary; 54 | 55 | return ctx; 56 | } 57 | -------------------------------------------------------------------------------- /templates/node/test/index.test.ts: -------------------------------------------------------------------------------- 1 | import type ParamsDictionary from "@nanoservice-ts/shared/dist/types/ParamsDictionary"; 2 | import { beforeAll, expect, test } from "vitest"; 3 | import Node from "../index"; 4 | import ctx from "./helper"; 5 | 6 | let node: Node; 7 | 8 | beforeAll(() => { 9 | node = new Node(); 10 | node.name = "api-call"; 11 | }); 12 | 13 | // Validate Hello World from Node 14 | test("Hello World from Node", async () => { 15 | const response = await node.handle(ctx(), {}); 16 | const message: ParamsDictionary = { message: "Hello World from Node!" }; 17 | 18 | expect(message).toEqual(response.data); 19 | }); 20 | -------------------------------------------------------------------------------- /templates/node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "sourceMap": true, 7 | "outDir": "./dist", 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "strict": true, 11 | "noUnusedLocals": true, 12 | "noImplicitReturns": true, 13 | "skipLibCheck": true 14 | }, 15 | "compileOnSave": true 16 | } 17 | -------------------------------------------------------------------------------- /templates/ts-template/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": ".ts,.js", 4 | "ignore": [], 5 | "exec": "npx ts-node ./src/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /templates/ts-template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-template", 3 | "version": "1.0.0", 4 | "description": "", 5 | "engines": { 6 | "node": ">=18.0.0" 7 | }, 8 | "main": "dist/index.js", 9 | "types": "dist/index.d.ts", 10 | "repository": "https://github.com/deskree-inc/nanoservice-ts-shared-lib.git", 11 | "author": "Deskree Technologies Inc.", 12 | "license": "Apache-2.0", 13 | "scripts": { 14 | "start:dev": "npx nodemon", 15 | "build": "rimraf ./dist && tsc" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "^22.13.4", 19 | "nodemon": "^3.1.9", 20 | "rimraf": "^6.0.1", 21 | "ts-node": "^10.9.2", 22 | "typescript": "^5.7.2" 23 | }, 24 | "dependencies": { 25 | "zod": "^3.24.1" 26 | }, 27 | "private": true 28 | } 29 | -------------------------------------------------------------------------------- /templates/ts-template/src/index.ts: -------------------------------------------------------------------------------- 1 | console.log("Hello from shared-lib"); 2 | -------------------------------------------------------------------------------- /templates/ts-template/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "rootDir": "./", 6 | "declaration": true, 7 | "sourceMap": true, 8 | "outDir": "./dist", 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "noUnusedLocals": true, 13 | "noImplicitReturns": true, 14 | "skipLibCheck": true 15 | }, 16 | "compileOnSave": true, 17 | "include": ["./src"] 18 | } 19 | -------------------------------------------------------------------------------- /triggers/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/triggers/.DS_Store -------------------------------------------------------------------------------- /triggers/grpc/.env.example: -------------------------------------------------------------------------------- 1 | PROJECT_NAME=trigger-grpc-server 2 | PROJECT_VERSION=0.0.1 3 | GRPC_PORT=8433 4 | GRPC_HOST=0.0.0.0 5 | WORKFLOWS_PATH=PROJECT_PATH/workflows 6 | NODES_PATH=PROJECT_PATH/src/nodes 7 | CONSOLE_LOG_ACTIVE=true 8 | APP_NAME=nanoservice-grpc -------------------------------------------------------------------------------- /triggers/grpc/README.md: -------------------------------------------------------------------------------- 1 | # Use the gRPC protocol instead of the Connect protocol 2 | 3 | On Node.js, we support three protocols: 4 | 5 | 1. The gRPC protocol that is used throughout the gRPC ecosystem. 6 | 2. The gRPC-Web protocol used by grpc/grpc-web, allowing servers to interop with grpc-web front-ends without the need for an intermediary proxy (such as Envoy). 7 | 3. The new Connect protocol, a simple, HTTP-based protocol that works over HTTP/1.1 or HTTP/2. It takes the best portions of gRPC and gRPC-Web, including streaming, and packages them into a protocol that works equally well in browsers, monoliths, and microservices. The Connect protocol is what we think the gRPC protocol should be. By default, JSON- and binary-encoded Protobuf is supported. 8 | 9 | So far, we have been using the ```http://``` scheme in our examples. We were not using TLS (Transport Layer Security). If you want to use gRPC and browser clients during local development, you need TLS. 10 | 11 | Actually, that only takes a minute to set up! We will use ```mkcert``` to make a certificate. If you don't have it installed yet, please run the following commands: 12 | 13 | ```bash 14 | brew install mkcert 15 | mkcert -install 16 | mkcert localhost 127.0.0.1 ::1 17 | export NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem" 18 | ``` 19 | 20 | If you don't use macOS or ```brew```, see the mkcert docs for instructions. You can copy the last line to your ```~/.zprofile``` or ```~/.profile```, so that the environment variable for Node.js is set every time you open a terminal. 21 | 22 | If you already use ```mkcert```, just run ```mkcert localhost 127.0.0.1 ::1``` to issue a certificate for our example server. 23 | 24 | Reference: 25 | https://connectrpc.com/docs/node/getting-started/ -------------------------------------------------------------------------------- /triggers/grpc/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | clean: true 3 | plugins: 4 | - local: protoc-gen-es 5 | opt: target=ts 6 | out: src/gen 7 | include_imports: true 8 | # - local: protoc-gen-connect-es 9 | # opt: target=ts 10 | # out: src 11 | # include_imports: true -------------------------------------------------------------------------------- /triggers/grpc/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | breaking: 3 | use: 4 | - FILE 5 | lint: 6 | use: 7 | - STANDARD 8 | modules: 9 | - path: proto -------------------------------------------------------------------------------- /triggers/grpc/localhost+2-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDNjfXRKBwXU0IH 3 | ftJJGiR5w6Ccf2JfPZJgDdarhVgJjL2cy9d4AwIq4ydiuU+H8MnguzdGrFaCgYOB 4 | GfgEI4dOAGr1AInRVVHRWKvwGdLYm59dBRi5PQuU2EKVonQNsZy/qcOJSBzBSSAx 5 | PFCpTwrxj0j/Dug6LxIa1wB7UenYn5NjkFSF8F5HufE7l4goIVh0/fVgRripCsnk 6 | iMQFVhMr+2glX+19zTVkBl9EtAPbkXsegUUdnkOH2sd1TBN4HUibnktX+P1aIlhd 7 | p741Eni42jU92u33iZasvvnkA94Q2MhBhzx3lgqAAcHcePzBI9yOEQbTqik4Nkpv 8 | moqqDFBbAgMBAAECggEACDu0aeFCZsjF70jsIVNzvJDSYRJVHV14/HHxdENOCGvj 9 | uGENy1fLgW5ByJmFjvtdCqxVBXanrGiFnOkuXvQRWlV380JHQuQ497qmgGJ4aIar 10 | Nm2a1OiWCysyV43/OgEwNxkUpO4HgNJuAKrazD1HQe+sS9MMCDCUeBC+izH3vN5s 11 | 9LWOrhde+ZwCkHJgRxp6V5HBJXU/27T02ZjgvPXupzXQ2ozRgx2gn5lLvJJfqG/h 12 | jEMi9qyuwoRUL4QXDg3TNHHSllnENdRrVJr7VsvYpRpQogVsr4V7zIZiFkAcbuC4 13 | kMuHeahkjqmlSYVuz9DxPHcmFRW/B5MI5MLQwCvI6QKBgQD61emfMrA323QIWF5l 14 | hbNQBS3hje9pwN27EU3QmCq0z/2Ja0TTaSeNSdlQAfMEGHsO5tL0ldqS267uqs5r 15 | clrukrOFmcARZiHne5U1uMcWgYvRmPTTdwng1lQSBuLo7XR9f6sW3u1csOJ8PY4w 16 | oKtsAIKm0r53iVRCPpd7D9Lr3QKBgQDRyWIccS93kI2rmRxkg4nhniCP8uAacYHy 17 | ZEZUFpm0p23rLKF7zlGd1xZmwKV5lnd1278qBt9YMvDGw6Mlt3WTQUh9XQ3b5q1V 18 | xOHfyn2QvWfFbX/tWousq2RP3CJakGiKMefSx8aZB/Vwat3kSdgz825Vcr9oN2j1 19 | lZZemRlllwKBgD+K1QXpN07PHxPGHxOkzQnmZfsPk6i8nO74sTe4dIkdb2LSc7sC 20 | lmxqWsyz0bYl5Xae7q3bEGXHlQcM6gG6FGL/cs7TZh6COvCDFAdp6bU/2AWes9al 21 | /zCf2ug+KllLXgk0wEio7IRgot9KUKzs5sATWQWduXZaqaemZ4VbOtlZAoGAK457 22 | b1pUt6dssq8khP4Gl7CEJD8SJotgppoJPBZBLZABs21dzd3FdlcdEJJ5COijvvW0 23 | GB67Ko3RDgO6kO8pWzyXt2elo/2eCYvKNfPN5EpfDE+y2u4Mx/oB5s7W1yTaf67i 24 | 6hGOe2F6pS+bCHYIjKjDRLyBXzr58NtxEA0Lh9sCgYEA6dI03OhJpruHxyOE3HYE 25 | oSDah3wr8WUw5BsGDsrW2tMVV7Y3YRO02mL/DB17bmM1FzS5s0n8DKU0c2wViFm4 26 | ZI4wZdm1hA1MBQBEFVQlVkmwtfyhmsnr98Huckd58PfMUZf6KJE9L4h7SXtPa/VL 27 | 492ASgwAEWWqFlfK6wSrHiM= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /triggers/grpc/localhost+2.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEoTCCAwmgAwIBAgIRAKSu5ey0gTHNC82jEhdfdpYwDQYJKoZIhvcNAQELBQAw 3 | gbExHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTFDMEEGA1UECww6bWFy 4 | Y29ATWFyY29zLU1hY0Jvb2stUHJvLmxvY2FsIChNYXJjbyBDYXN0aWxsbyBEZWxs 5 | YSBTZXJhKTFKMEgGA1UEAwxBbWtjZXJ0IG1hcmNvQE1hcmNvcy1NYWNCb29rLVBy 6 | by5sb2NhbCAoTWFyY28gQ2FzdGlsbG8gRGVsbGEgU2VyYSkwHhcNMjUwMjAzMDE1 7 | MjM5WhcNMjcwNTAzMDE1MjM5WjBuMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1l 8 | bnQgY2VydGlmaWNhdGUxQzBBBgNVBAsMOm1hcmNvQE1hcmNvcy1NYWNCb29rLVBy 9 | by5sb2NhbCAoTWFyY28gQ2FzdGlsbG8gRGVsbGEgU2VyYSkwggEiMA0GCSqGSIb3 10 | DQEBAQUAA4IBDwAwggEKAoIBAQDNjfXRKBwXU0IHftJJGiR5w6Ccf2JfPZJgDdar 11 | hVgJjL2cy9d4AwIq4ydiuU+H8MnguzdGrFaCgYOBGfgEI4dOAGr1AInRVVHRWKvw 12 | GdLYm59dBRi5PQuU2EKVonQNsZy/qcOJSBzBSSAxPFCpTwrxj0j/Dug6LxIa1wB7 13 | UenYn5NjkFSF8F5HufE7l4goIVh0/fVgRripCsnkiMQFVhMr+2glX+19zTVkBl9E 14 | tAPbkXsegUUdnkOH2sd1TBN4HUibnktX+P1aIlhdp741Eni42jU92u33iZasvvnk 15 | A94Q2MhBhzx3lgqAAcHcePzBI9yOEQbTqik4NkpvmoqqDFBbAgMBAAGjdjB0MA4G 16 | A1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBTN 17 | imcw8dRZ+LdM83iq2xnhRgHVMjAsBgNVHREEJTAjgglsb2NhbGhvc3SHBH8AAAGH 18 | EAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggGBAIA2HiGT0dtdkNfB 19 | LFAIUvlatqHkH4Pds3jd0TnT4muoebPoHc3HSoqnSdt4g2vN+75S47G7E5b/qQbH 20 | ZfkL9kgZoCar79DuVpC/kCPI/NACRsqdgIcnd09FqN2Dmg1UP2F/nulLucWfOiSL 21 | T3q3nVEAm3UL4R5VopFW94bp8jJ/cWseDohIGZBza4yUiMnbd6mK+g+DoExfGbwc 22 | A3601xeDWcDxKv+KaBWQeyoUWO66FKYOJcdwaiPCPrvXVz3tdbxBpHB/beNeulaU 23 | 6EzDRCmtIwEZNwq9MNn0wViOjta1TtkeERtbvnmgPmFhhSbHDtGbbxuwWnV91aZg 24 | 0JFymKsmmBIv4Qu50zVPo7Jz0n6CucIPVUW8ZoamVBr6ljHOfZIg9N0hkRoX7DUT 25 | pPV22KliCRZCHYnyVcIzwtW2lxlzydUnldhxZga6vlfoKR5WzCceTtsX2+p8dbJe 26 | p5jVRsLyT/sfECb8n5N7BYRLSh36FekFGErFCr9Svqd9pRVsWg== 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /triggers/grpc/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts", 4 | "ignore": ["src/**/*.test.ts"], 5 | "exec_metrics": "node -r ts-node/register -r ./src/opentelemetry_metrics.ts --env-file=.env.local ./src/server.ts", 6 | "exec_mkcert": "export NODE_EXTRA_CA_CERTS=\"$(mkcert -CAROOT)/rootCA.pem\" && node -r ts-node/register --env-file=.env.local ./src/server.ts", 7 | "exec": "node -r ts-node/register -r ./src/opentelemetry_metrics.ts --env-file=.env.local ./src/server.ts", 8 | "env": { 9 | "NODE_ENV": "local" 10 | }, 11 | "ignoreRoot": [".git"] 12 | } 13 | -------------------------------------------------------------------------------- /triggers/grpc/proto/workflow.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package nanoservice.workflow.v1; 4 | 5 | message WorkflowRequest { 6 | string Name = 1; 7 | string Message = 2; 8 | string Encoding = 3; 9 | string Type = 4; 10 | } 11 | 12 | message WorkflowResponse { 13 | string Message = 1; 14 | string Encoding = 2; 15 | string Type = 3; 16 | } 17 | 18 | enum MessageEncoding { 19 | BASE64 = 0; 20 | STRING = 1; 21 | } 22 | 23 | enum MessageType { 24 | TEXT = 0; 25 | JSON = 1; 26 | XML = 2; 27 | HTML = 3; 28 | BINARY = 4; 29 | } 30 | 31 | service WorkflowService { 32 | rpc ExecuteWorkflow (WorkflowRequest) returns (WorkflowResponse) {} 33 | } -------------------------------------------------------------------------------- /triggers/grpc/src/Nodes.ts: -------------------------------------------------------------------------------- 1 | import ApiCall from "@nanoservice-ts/api-call"; 2 | import IfElse from "@nanoservice-ts/if-else"; 3 | import type { NodeBase } from "@nanoservice-ts/shared"; 4 | 5 | const nodes: { 6 | [key: string]: NodeBase; 7 | } = { 8 | "@nanoservice-ts/api-call": new ApiCall(), 9 | "@nanoservice-ts/if-else": new IfElse(), 10 | }; 11 | 12 | export default nodes; 13 | -------------------------------------------------------------------------------- /triggers/grpc/src/Workflows.ts: -------------------------------------------------------------------------------- 1 | import type Workflows from "./types/Workflows"; 2 | 3 | const workflows: Workflows = {}; 4 | 5 | export default workflows; 6 | -------------------------------------------------------------------------------- /triggers/grpc/src/index.ts: -------------------------------------------------------------------------------- 1 | import GrpcClient from "./GrpcClient"; 2 | import { RpcOptions } from "./GrpcClient"; 3 | import { CallOptions } from "./GrpcClient"; 4 | import { TransportEnum } from "./GrpcClient"; 5 | import { HttpVersionEnum } from "./GrpcClient"; 6 | import GrpcServer from "./GrpcServer"; 7 | import { GrpcServerOptions } from "./GrpcServer"; 8 | import NanoSDK from "./NanoSDK"; 9 | import { WorkflowRequest, WorkflowResponse } from "./gen/workflow_pb"; 10 | 11 | export { 12 | GrpcClient, 13 | RpcOptions, 14 | CallOptions, 15 | TransportEnum, 16 | HttpVersionEnum, 17 | WorkflowRequest, 18 | WorkflowResponse, 19 | GrpcServer, 20 | GrpcServerOptions, 21 | NanoSDK, 22 | }; 23 | -------------------------------------------------------------------------------- /triggers/grpc/src/opentelemetry_metrics.ts: -------------------------------------------------------------------------------- 1 | import { DefaultLogger } from "@nanoservice-ts/runner"; 2 | import { metrics } from "@opentelemetry/api"; 3 | import { PrometheusExporter } from "@opentelemetry/exporter-prometheus"; 4 | import { Resource } from "@opentelemetry/resources"; 5 | import { MeterProvider } from "@opentelemetry/sdk-metrics"; 6 | import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions"; 7 | 8 | // Set up the Prometheus exporter to expose metrics at /metrics on port 9091 9 | const prometheusExporter = new PrometheusExporter({ port: 9091, endpoint: "/metrics" }, () => 10 | new DefaultLogger().log("Prometheus scrape endpoint: http://localhost:9091/metrics"), 11 | ); 12 | 13 | const resource = Resource.default().merge( 14 | new Resource({ 15 | [ATTR_SERVICE_NAME]: "trigger-http", 16 | [ATTR_SERVICE_VERSION]: "0.0.8", 17 | }), 18 | ); 19 | 20 | // Creates MeterProvider and installs the exporter as a MetricReader 21 | const meterProvider = new MeterProvider({ 22 | resource: resource, 23 | }); 24 | meterProvider.addMetricReader(prometheusExporter); 25 | 26 | metrics.setGlobalMeterProvider(meterProvider); 27 | -------------------------------------------------------------------------------- /triggers/grpc/src/server.ts: -------------------------------------------------------------------------------- 1 | import GrpcServer from "./GrpcServer"; 2 | 3 | const server = new GrpcServer({ 4 | host: "0.0.0.0", 5 | port: 8443, 6 | }); 7 | 8 | server.start(); 9 | -------------------------------------------------------------------------------- /triggers/grpc/src/types/Message.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/triggers/grpc/src/types/Message.ts -------------------------------------------------------------------------------- /triggers/grpc/src/types/RuntimeWorkflow.ts: -------------------------------------------------------------------------------- 1 | import type { ParamsDictionary } from "@nanoservice-ts/runner"; 2 | 3 | type RuntimeWorkflow = { 4 | workflow: ParamsDictionary; 5 | }; 6 | 7 | export default RuntimeWorkflow; 8 | -------------------------------------------------------------------------------- /triggers/grpc/src/types/Workflows.ts: -------------------------------------------------------------------------------- 1 | import type { HelperResponse } from "@nanoservice-ts/helper"; 2 | 3 | type Workflows = { 4 | [key: string]: HelperResponse; 5 | }; 6 | 7 | export default Workflows; 8 | -------------------------------------------------------------------------------- /triggers/grpc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "CommonJS", 5 | "rootDir": "./src", 6 | "baseUrl": ".", 7 | "paths": { 8 | "@nodes/*": ["./src/nodes/*"], 9 | "@src/*": ["src/*"] 10 | }, 11 | "allowJs": true, 12 | "declaration": true, 13 | "sourceMap": true, 14 | "outDir": "./dist", 15 | "esModuleInterop": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noImplicitReturns": true, 20 | "skipLibCheck": true 21 | }, 22 | "compileOnSave": true, 23 | "include": [ 24 | "./src", 25 | "./src/nodes/**/*.json", 26 | "./src/nodes/**/*.md", 27 | "./src/*.json" 28 | ], 29 | "exclude": ["src/nodes/**/test/*.ts", "node_modules"] 30 | } 31 | -------------------------------------------------------------------------------- /triggers/grpc/workflows/countries.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "World Countries", 3 | "description": "Workflow description", 4 | "version": "1.0.0", 5 | "trigger": { 6 | "http": { 7 | "method": "GET", 8 | "path": "/", 9 | "accept": "application/json" 10 | } 11 | }, 12 | "steps": [ 13 | { 14 | "name": "get-countries-api", 15 | "node": "@nanoservice-ts/api-call", 16 | "type": "module" 17 | } 18 | ], 19 | "nodes": { 20 | "get-countries-api": { 21 | "inputs": { 22 | "url": "https://countriesnow.space/api/v0.1/countries/capital", 23 | "method": "GET", 24 | "headers": { 25 | "Content-Type": "application/json" 26 | }, 27 | "responseType": "application/json" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /triggers/http/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/triggers/http/.DS_Store -------------------------------------------------------------------------------- /triggers/http/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | Dockerfile* 3 | docker-compose* 4 | .dockerignore 5 | .git 6 | .gitignore 7 | README.md 8 | LICENSE 9 | .vscode 10 | Makefile 11 | helm-charts 12 | .env 13 | .editorconfig 14 | .idea 15 | coverage* 16 | runtimes 17 | .DS_Store 18 | *.log 19 | .nanoctl/runtimes/python3/python3_runtime 20 | .nanoctl/runtimes/python3/tests 21 | .nanoctl/runtimes/python3/Makefile 22 | .nanoctl/runtimes/python3/nodemon.json 23 | .nanoctl/runtimes/python3/node_modules -------------------------------------------------------------------------------- /triggers/http/.env.example: -------------------------------------------------------------------------------- 1 | PROJECT_NAME=trigger-http-server 2 | PROJECT_VERSION=0.0.1 3 | PORT=4000 4 | WORKFLOWS_PATH=PROJECT_PATH/workflows 5 | NODES_PATH=PROJECT_PATH/src/nodes 6 | CONSOLE_LOG_ACTIVE=true 7 | APP_NAME=nanoservice-http 8 | DISABLE_TRIGGER_RUN=false # Set to true to disable trigger run and use this project as a module -------------------------------------------------------------------------------- /triggers/http/Dockerfile: -------------------------------------------------------------------------------- 1 | # use the official Bun image 2 | # see all versions at https://hub.docker.com/r/oven/bun/tags 3 | FROM oven/bun:1 AS base 4 | WORKDIR /usr/src/app 5 | 6 | # install dependencies into temp directory 7 | # this will cache them and speed up future builds 8 | FROM base AS install 9 | RUN mkdir -p /temp/dev 10 | COPY package.json /temp/dev/ 11 | RUN cd /temp/dev && bun install --frozen-lockfile 12 | 13 | # install with --production (exclude devDependencies) 14 | RUN mkdir -p /temp/prod 15 | COPY package.json /temp/prod/ 16 | RUN cd /temp/prod && bun install --frozen-lockfile --production 17 | 18 | # copy node_modules from temp directory 19 | # then copy all (non-ignored) project files into the image 20 | FROM base AS prerelease 21 | COPY --from=install /temp/dev/node_modules node_modules 22 | COPY . . 23 | 24 | # [optional] tests & build 25 | ENV NODE_ENV=production 26 | # RUN bun add -d @types/ejs 27 | RUN bun run build 28 | 29 | # copy production dependencies and source code into final image 30 | FROM node:23.11.1-slim AS release 31 | WORKDIR /usr/src/app 32 | 33 | COPY --from=install /temp/prod/node_modules node_modules 34 | COPY --from=prerelease /usr/src/app/dist dist 35 | COPY --from=prerelease /usr/src/app/package.json . 36 | COPY --from=prerelease /usr/src/app/workflows workflows 37 | COPY --from=prerelease /usr/src/app/public public 38 | 39 | ENV WORKFLOWS_PATH=/usr/src/app/workflows 40 | ENV CONSOLE_LOG_ACTIVE=true 41 | ENV NODE_ENV=production 42 | ENV APP_NAME=nanoservice-http 43 | 44 | # run the app 45 | USER node 46 | EXPOSE 4000/tcp 47 | EXPOSE 9091/tcp 48 | 49 | ENTRYPOINT [ "node", "-r", "./dist/runner/metrics/opentelemetry_metrics.js", "dist/index.js" ] -------------------------------------------------------------------------------- /triggers/http/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | # Stage 1: Use Bun to install dependencies 2 | FROM oven/bun:1.1.4-alpine AS deps 3 | WORKDIR /app 4 | 5 | COPY package.json *bun.lockb ./ 6 | RUN bun install --production 7 | 8 | # Stage 2: Use Node.js to run the app 9 | FROM node:23-alpine AS runner 10 | WORKDIR /usr/src/app 11 | 12 | # Copy installed node_modules from previous stage 13 | COPY --from=deps /app/node_modules ./node_modules 14 | 15 | COPY *dist ./dist 16 | COPY workflows ./workflows 17 | COPY public ./public 18 | COPY package.json package.json 19 | COPY tsconfig.json tsconfig.json 20 | 21 | ENV NODE_ENV=production 22 | ENV PROJECT_NAME=trigger-http-server 23 | ENV PROJECT_VERSION=0.0.1 24 | ENV APP_NAME=nanoservice-http 25 | ENV PORT=4000 26 | ENV WORKFLOWS_PATH=/usr/src/app/workflows 27 | ENV NODES_PATH=/usr/src/app/src/nodes 28 | ENV DISABLE_TRIGGER_RUN=false 29 | 30 | # Expose app ports 31 | EXPOSE 4000 9091 32 | 33 | # Wait for dist/index.js to be compiled before running 34 | CMD ["sh", "-c", "while [ ! -f dist/index.js ]; do echo -n '.'; sleep 1; done && echo && node -r ./dist/runner/metrics/opentelemetry_metrics.js dist/index.js"] 35 | -------------------------------------------------------------------------------- /triggers/http/infra/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | nanoservice-http: 3 | container_name: nanoservice-http 4 | hostname: nanoservice-http 5 | build: 6 | context: ../ 7 | dockerfile: Dockerfile.dev 8 | ports: 9 | - "4000:4000" 10 | - "9091:9091" 11 | develop: 12 | watch: 13 | - action: sync 14 | path: ../dist 15 | target: /usr/src/app/dist 16 | - action: sync 17 | path: ../workflows 18 | target: /usr/src/app/workflows 19 | - action: sync 20 | path: ../public 21 | target: /usr/src/app/public 22 | - action: rebuild 23 | path: ../package.json 24 | - action: rebuild 25 | path: ../tsconfig.json 26 | - action: rebuild 27 | path: ../.env.local 28 | networks: 29 | - shared-network 30 | # logging: # Log to Loki. Enable this to see logs in Grafana. 31 | # driver: loki 32 | # options: 33 | # loki-url: "http://localhost:3100/loki/api/v1/push" 34 | # loki-retries: 5 35 | # loki-batch-size: 400 36 | networks: 37 | shared-network: 38 | external: true 39 | -------------------------------------------------------------------------------- /triggers/http/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts", 4 | "ignore": ["src/**/*.test.ts"], 5 | "exec_simple": "node -r ts-node/register --env-file=.env.local ./src/index.ts", 6 | "exec_metrics": "node -r ts-node/register -r ./src/runner/metrics/opentelemetry_metrics.ts --env-file=.env.local ./src/index.ts", 7 | "exec_tracing": "node -r ts-node/register -r ./src/runner/metrics/opentelemetry_traces.ts --env-file=.env.local ./src/index.ts", 8 | "exec_metrics_tracing": "node -r ts-node/register -r ./src/runner/metrics/opentelemetry_metrics.ts -r ./src/runner/metrics/opentelemetry_traces.ts --env-file=.env.local ./src/index.ts", 9 | "exec": "node -r ts-node/register --env-file=.env.local ./src/index.ts", 10 | "env": { 11 | "NODE_ENV": "local" 12 | }, 13 | "ignoreRoot": [".git"] 14 | } 15 | -------------------------------------------------------------------------------- /triggers/http/notes/CLI_Commands/Building.md: -------------------------------------------------------------------------------- 1 | # NanoService CLI Commands: Build 2 | 3 | ## Table of Contents 4 | - [Build Nanoservice](#build-nanoservice) 5 | - [Syntax](#build-syntax) 6 | - [Options](#build-options) 7 | - [Examples](#build-examples) 8 | - [Deploy Nanoservice](#deploy-nanoservice) 9 | - [Syntax](#deploy-syntax) 10 | - [Options](#deploy-options) 11 | - [Examples](#deploy-examples) 12 | - [Common Examples](#common-examples) 13 | - [Troubleshooting](#troubleshooting) 14 | 15 | --- 16 | 17 | ## Build Nanoservice 18 | 19 | ### Syntax 20 | ```bash 21 | npx nanoctl build [options] 22 | ``` 23 | Compiles and packages a nanoservice from source code into a deployable artifact. 24 | 25 | ### Options 26 | | Option | Alias | Type | Description | Default | 27 | |--------------|-------|---------|----------------------------|--------------------| 28 | | `--directory`| `-d` | string | Source directory path | `./nanoservice-ts` | 29 | | `--help` | `-h` | boolean | Show help | `false` | 30 | 31 | ### Commands 32 | | Command | Description | 33 | |---------|---------------------------| 34 | | `.` | Build in current directory| 35 | 36 | ### Examples 37 | #### Build in default directory: 38 | ``` bash 39 | npx nanoctl build 40 | ``` 41 | #### Build in specific directory: 42 | ```bash 43 | npx nanoctl build -d ./my-nanoservice 44 | ``` 45 | 46 | #### Build in current directory: 47 | ```bash 48 | npx nanoctl build . 49 | ``` 50 | 51 | --- 52 | > **Note**: After executing the building command, you can proceed to deploy the nanoservice using the [deploy](./Deployment.md) command. -------------------------------------------------------------------------------- /triggers/http/notes/CLI_Commands/Deployment.md: -------------------------------------------------------------------------------- 1 | # NanoService CLI Commands 2 | 3 | ## Table of Contents 4 | 5 | --- 6 | 7 | ## Deploy Nanoservice 8 | 9 | ### Syntax 10 | ```bash 11 | npx nanoctl deploy [options] 12 | ``` 13 | 14 | 15 | Deploys a nanoservice to the NanoServices platform, optionally building it first. 16 | * 17 | Options: 18 | - `--name`, `-n` (string): Service name (required). Default: none. 19 | - `--build` (boolean): Build before deploying. Default: false. 20 | - `--directory`, `-d` (string): Source directory path. Default: Current working directory. 21 | - `--help`, `-h` (boolean): Show help. Default: false. 22 | * 23 | Commands: 24 | - `.`: Deploy from the current directory. 25 | 26 | ### Examples 27 | 28 | #### Deploy with build: 29 | ```bash 30 | npx nanoctl deploy -n my-service --build 31 | ``` 32 | 33 | #### Deploy existing build: 34 | ```bash 35 | npx nanoctl deploy -n my-service -d ./build-output 36 | ``` 37 | 38 | #### Deploy from current directory: 39 | ```bash 40 | npx nanoctl deploy -n my-service . 41 | ``` 42 | 43 |
44 |
45 |
46 | 47 | --- 48 | # Build and Deploy Example 49 | 50 | #### Build and deploy workflow: 51 | ```bash 52 | # Build first 53 | npx nanoctl build -d ./src 54 | 55 | # Then deploy 56 | npx nanoctl deploy -n my-service -d ./src 57 | ``` 58 | 59 | #### Single-command build & deploy: 60 | ```bash 61 | npx nanoctl deploy -n my-service --build 62 | ``` 63 | 64 | #### CI/CD Pipeline Example: 65 | ```bash 66 | # In your deployment script 67 | npx nanoctl build -d $CODE_DIR 68 | npx nanoctl deploy -n $SERVICE_NAME -d $CODE_DIR 69 | ``` -------------------------------------------------------------------------------- /triggers/http/notes/index.md: -------------------------------------------------------------------------------- 1 | # Quick Start Guide 2 | 3 | ## 1. Setting Up Your Environment 4 | 5 | Get started with `nanoservice-ts` by following these steps: 6 | 7 | ### Prerequisites 8 | Ensure the following are installed: 9 | - **Node.js** (v22.14.0 LTS recommended) 10 | 11 | ### Step 1: Create a New Project 12 | Run the command below to create a project: 13 | ```sh 14 | npx nanoctl@latest create project 15 | ``` 16 | Follow the prompts: 17 | 1. Assign a project name. 18 | 2. Select **HTTP trigger** (default). 19 | 3. Choose **NodeJS** as the runtime (Python3 is in alpha). 20 | 4. **Select Example Installation: YES** (recommended for discovery and learning). 21 | 22 | ### Step 2: Navigate to Your Project and Start the Server 23 | ```sh 24 | cd your-project-name 25 | pnpm run dev 26 | ``` 27 | Your server will be running at: 28 | `http://localhost:4000` 29 | 30 | --- 31 | 32 | ## 2. Running the Example Workflows 33 | 34 | Explore the provided example workflows to get started. Access them via: 35 | 36 | - **Countries API**: 37 | `http://localhost:4000/countries` 38 | 39 | --- 40 | 41 | ## 🔍 Next Steps 42 | 43 | Now that you understand the basics, dive deeper into `nanoservice-ts` with the following resources: 44 | 45 | - [Context](./Core_Concepts/Context.md) 46 | - [Creating Workflows](./CLI_Commands/Create_Workflow.md) 47 | - [Creating Custom Nodes](./CLI_Commands/Create_Node.md) 48 | - [Examples](./examples.md) 49 | - [Authentication](./CLI_Commands/Authentication.md) 50 | - [Building](./CLI_Commands/Building.md) 51 | - [Deployment](./CLI_Commands/Deployment.md) -------------------------------------------------------------------------------- /triggers/http/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deskree-inc/nanoservice-ts/af8d6f6c4aebce8108a255d1fcc8c1520f8123b5/triggers/http/public/favicon.ico -------------------------------------------------------------------------------- /triggers/http/request.http: -------------------------------------------------------------------------------- 1 | GET http://localhost:4000/countries HTTP/1.1 2 | 3 | ### 4 | 5 | GET http://localhost:4000/empty HTTP/1.1 6 | 7 | ### 8 | -------------------------------------------------------------------------------- /triggers/http/src/Nodes.ts: -------------------------------------------------------------------------------- 1 | import ApiCall from "@nanoservice-ts/api-call"; 2 | import IfElse from "@nanoservice-ts/if-else"; 3 | import type { NodeBase } from "@nanoservice-ts/shared"; 4 | 5 | const nodes: { 6 | [key: string]: NodeBase; 7 | } = { 8 | "@nanoservice-ts/api-call": new ApiCall(), 9 | "@nanoservice-ts/if-else": new IfElse(), 10 | }; 11 | 12 | export default nodes; 13 | -------------------------------------------------------------------------------- /triggers/http/src/Workflows.ts: -------------------------------------------------------------------------------- 1 | import type Workflows from "./runner/types/Workflows"; 2 | import countriesFactsHelper from "./workflows/countries-cats-helper"; 3 | import countriesHelper from "./workflows/countries-helper"; 4 | import empty from "./workflows/empty"; 5 | 6 | const workflows: Workflows = { 7 | "countries-helper": countriesHelper, 8 | "countries-cats-helper": countriesFactsHelper, 9 | "empty-helper": empty, 10 | }; 11 | 12 | export default workflows; 13 | -------------------------------------------------------------------------------- /triggers/http/src/index.ts: -------------------------------------------------------------------------------- 1 | import { DefaultLogger } from "@nanoservice-ts/runner"; 2 | import { type Span, metrics, trace } from "@opentelemetry/api"; 3 | import HttpTrigger from "./runner/HttpTrigger"; 4 | 5 | export default class App { 6 | private httpTrigger: HttpTrigger = {}; 7 | protected trigger_initializer = 0; 8 | protected initializer = 0; 9 | protected tracer = trace.getTracer( 10 | process.env.PROJECT_NAME || "trigger-http-server", 11 | process.env.PROJECT_VERSION || "0.0.1", 12 | ); 13 | private logger = new DefaultLogger(); 14 | protected app_cold_start = metrics.getMeter("default").createGauge("initialization", { 15 | description: "Application cold start", 16 | }); 17 | 18 | constructor() { 19 | this.initializer = performance.now(); 20 | this.httpTrigger = new HttpTrigger(); 21 | } 22 | 23 | async run() { 24 | this.tracer.startActiveSpan("initialization", async (span: Span) => { 25 | await this.httpTrigger.listen(); 26 | this.initializer = performance.now() - this.initializer; 27 | 28 | this.logger.log(`Server initialized in ${(this.initializer).toFixed(2)}ms`); 29 | this.app_cold_start.record(this.initializer, { 30 | pid: process.pid, 31 | env: process.env.NODE_ENV, 32 | app: process.env.APP_NAME, 33 | }); 34 | span.end(); 35 | }); 36 | } 37 | 38 | // Expose the Express app for hosting with serverless functions like AWS Lambda, GC Functions, etc. 39 | getHttpApp() { 40 | return this.httpTrigger.getApp(); 41 | } 42 | } 43 | 44 | if (process.env.DISABLE_TRIGGER_RUN !== "true") { 45 | new App().run(); 46 | } 47 | -------------------------------------------------------------------------------- /triggers/http/src/nodes/examples/dashboard-generator/ArrayMap.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type INanoServiceResponse, 3 | type JsonLikeObject, 4 | NanoService, 5 | NanoServiceResponse, 6 | type ParamsDictionary, 7 | } from "@nanoservice-ts/runner"; 8 | import { type Context, GlobalError } from "@nanoservice-ts/shared"; 9 | 10 | type InputType = { 11 | array: Array; 12 | map: string; 13 | }; 14 | 15 | export default class ArrayMapNode extends NanoService { 16 | constructor() { 17 | super(); 18 | this.inputSchema = { 19 | $schema: "http://json-schema.org/draft-04/schema#", 20 | type: "object", 21 | properties: { 22 | array: { type: "array" }, 23 | map: { type: "string" }, 24 | }, 25 | required: ["array", "map"], 26 | }; 27 | } 28 | 29 | async handle(ctx: Context, inputs: InputType): Promise { 30 | const response: NanoServiceResponse = new NanoServiceResponse(); 31 | 32 | try { 33 | if (!Array.isArray(inputs.array)) throw new Error("Array is not an array"); 34 | 35 | const result = inputs.array.map((data) => { 36 | return this.runJs(inputs.map, ctx, data as unknown as ParamsDictionary, undefined, ctx.vars); 37 | }); 38 | 39 | response.setSuccess(result as unknown as JsonLikeObject); 40 | } catch (error: unknown) { 41 | const nodeError = new GlobalError((error as Error).message); 42 | nodeError.setCode(500); 43 | response.setError(nodeError); 44 | } 45 | 46 | return response; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /triggers/http/src/nodes/examples/dashboard-generator/InMemory.ts: -------------------------------------------------------------------------------- 1 | import type { JsonLikeObject } from "@nanoservice-ts/runner"; 2 | 3 | export default class InMemory { 4 | protected static _instance: InMemory; 5 | protected data: Record = {}; 6 | 7 | public static getInstance(): InMemory { 8 | if (!InMemory._instance) { 9 | InMemory._instance = new InMemory(); 10 | } 11 | return InMemory._instance; 12 | } 13 | 14 | get(key: string): string | JsonLikeObject | null { 15 | return this.data[key] ?? null; 16 | } 17 | getAll(): Record { 18 | return this.data; 19 | } 20 | set(key: string, value: string | JsonLikeObject): void { 21 | this.data[key] = value; 22 | } 23 | delete(key: string): void { 24 | delete this.data[key]; 25 | } 26 | clear(): void { 27 | this.data = {}; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /triggers/http/src/nodes/examples/db-manager/MapperNode.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type INanoServiceResponse, 3 | type JsonLikeObject, 4 | NanoService, 5 | NanoServiceResponse, 6 | } from "@nanoservice-ts/runner"; 7 | import { type Context, GlobalError } from "@nanoservice-ts/shared"; 8 | 9 | type InputType = { 10 | model: object; 11 | }; 12 | 13 | export default class MapperNode extends NanoService { 14 | constructor() { 15 | super(); 16 | this.inputSchema = { 17 | $schema: "http://json-schema.org/draft-04/schema#", 18 | type: "object", 19 | properties: { 20 | model: { type: "object" }, 21 | }, 22 | required: ["model"], 23 | }; 24 | } 25 | 26 | async handle(ctx: Context, inputs: InputType): Promise { 27 | const response: NanoServiceResponse = new NanoServiceResponse(); 28 | 29 | try { 30 | response.setSuccess(inputs.model as JsonLikeObject); 31 | } catch (error: unknown) { 32 | const nodeError = new GlobalError((error as Error).message); 33 | nodeError.setCode(500); 34 | response.setError(nodeError); 35 | } 36 | 37 | return response; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /triggers/http/src/nodes/examples/mastra-agent/README.md: -------------------------------------------------------------------------------- 1 | # MASTRA-AGENT 2 | 3 | This nanoservice example implement Mastra Framework. 4 | 5 | ### Requirement 6 | 7 | This nanoservice require Bun.sh Javascript Runtime. Otherwise you will get an error: "This node must be executed with BUN". -------------------------------------------------------------------------------- /triggers/http/src/nodes/examples/mastra-agent/ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%=title%> 8 | 9 | 10 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | <%-react_script%> 20 | 21 | -------------------------------------------------------------------------------- /triggers/http/src/nodes/examples/mastra-agent/ui/inputSchema.ts: -------------------------------------------------------------------------------- 1 | export const inputSchema = { 2 | $schema: "http://json-schema.org/draft-07/schema#", 3 | title: "Generated schema for Root", 4 | type: "object", 5 | properties: { 6 | title: { 7 | type: "string", 8 | }, 9 | view_path: { 10 | type: "string", 11 | }, 12 | file_path: { 13 | type: "string", 14 | }, 15 | }, 16 | required: ["title", "file_path"], 17 | }; 18 | -------------------------------------------------------------------------------- /triggers/http/src/nodes/examples/mastra-agent/util.ts: -------------------------------------------------------------------------------- 1 | export default async function importEsModule(name: string) { 2 | const mastra = await import(name); 3 | return mastra; 4 | } 5 | -------------------------------------------------------------------------------- /triggers/http/src/nodes/examples/postgres-query/README.md: -------------------------------------------------------------------------------- 1 | # POSTGRES NODE 2 | 3 | This nanoservice execute queries with POSTGRES. The workflow used to execute this nanoservice is: 4 | 5 | 1. films.json 6 | 7 | ## Configuration 8 | 9 | ```json 10 | "get-films": { 11 | "inputs": { 12 | "host": "localhost", 13 | "port": 5432, 14 | "database": "dvdrental", 15 | "user": "postgres", 16 | "password": "example", 17 | "query": "SELECT * FROM \"film\" ORDER BY \"title\" LIMIT 50" 18 | } 19 | } 20 | ``` 21 | 22 | ## Deploy the Default Database 23 | 24 | To start testing this example, you need a Postgres Database available. Or just create it using our docker-compose.yml example. 25 | 26 | ```sh 27 | cd infra/development 28 | docker compose up -d 29 | ``` 30 | 31 | The database is created including tables and records. -------------------------------------------------------------------------------- /triggers/http/src/nodes/examples/workflow-docs/DirectoryManager.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import { 3 | type INanoServiceResponse, 4 | type JsonLikeObject, 5 | NanoService, 6 | NanoServiceResponse, 7 | } from "@nanoservice-ts/runner"; 8 | import { type Context, GlobalError } from "@nanoservice-ts/shared"; 9 | 10 | type InputType = { 11 | path: string; 12 | }; 13 | 14 | export default class DirectoryManager extends NanoService { 15 | constructor() { 16 | super(); 17 | this.inputSchema = { 18 | $schema: "http://json-schema.org/draft-04/schema#", 19 | type: "object", 20 | properties: { 21 | path: { type: "string" }, 22 | }, 23 | required: ["path"], 24 | }; 25 | } 26 | 27 | async handle(ctx: Context, inputs: InputType): Promise { 28 | const response: NanoServiceResponse = new NanoServiceResponse(); 29 | 30 | try { 31 | const files: string[] = fs.readdirSync(inputs.path); 32 | response.setSuccess({ 33 | path: inputs.path, 34 | files: files as unknown as JsonLikeObject[], 35 | }); 36 | } catch (error: unknown) { 37 | const nodeError = new GlobalError((error as Error).message); 38 | nodeError.setCode(500); 39 | response.setError(nodeError); 40 | } 41 | 42 | return response; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /triggers/http/src/nodes/examples/workflow-docs/ErrorNode.ts: -------------------------------------------------------------------------------- 1 | import { type INanoServiceResponse, NanoService, NanoServiceResponse } from "@nanoservice-ts/runner"; 2 | import { type Context, GlobalError } from "@nanoservice-ts/shared"; 3 | 4 | type ErrorNodeInputs = { 5 | message: string; 6 | }; 7 | 8 | export default class ErrorNode extends NanoService { 9 | constructor() { 10 | super(); 11 | 12 | // Set the input "JSON Schema Format" here for automated validation 13 | // Learn JSON Schema: https://json-schema.org/learn/getting-started-step-by-step 14 | this.inputSchema = { 15 | $schema: "http://json-schema.org/draft-07/schema#", 16 | title: "Generated schema for Root", 17 | type: "object", 18 | properties: { 19 | message: { 20 | type: "string", 21 | }, 22 | }, 23 | required: ["message"], 24 | }; 25 | 26 | // Set the output "JSON Schema Format" here for automated validation 27 | // Learn JSON Schema: https://json-schema.org/learn/getting-started-step-by-step 28 | this.outputSchema = {}; 29 | 30 | // Set html content type 31 | this.contentType = "text/html"; 32 | } 33 | 34 | async handle(ctx: Context, inputs: ErrorNodeInputs): Promise { 35 | // Create a new instance of the response 36 | const response = new NanoServiceResponse(); 37 | const message = inputs.message as string; 38 | 39 | try { 40 | throw new Error(message); 41 | } catch (error: unknown) { 42 | const nodeError: GlobalError = new GlobalError((error as Error).message); 43 | nodeError.setCode(500); 44 | nodeError.setStack((error as Error).stack); 45 | nodeError.setName(this.name); 46 | response.setError(nodeError); // Set the error 47 | } 48 | 49 | return response; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /triggers/http/src/nodes/examples/workflow-docs/FileManager.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import { type INanoServiceResponse, NanoService, NanoServiceResponse } from "@nanoservice-ts/runner"; 3 | import { type Context, GlobalError } from "@nanoservice-ts/shared"; 4 | 5 | type InputType = { 6 | path: string; 7 | }; 8 | 9 | export default class FileManager extends NanoService { 10 | constructor() { 11 | super(); 12 | this.inputSchema = { 13 | $schema: "http://json-schema.org/draft-04/schema#", 14 | type: "object", 15 | properties: { 16 | path: { type: "string" }, 17 | }, 18 | required: ["path"], 19 | }; 20 | } 21 | 22 | async handle(ctx: Context, inputs: InputType): Promise { 23 | const response: NanoServiceResponse = new NanoServiceResponse(); 24 | 25 | try { 26 | const content: string = fs.readFileSync(inputs.path, { encoding: "utf8", flag: "r" }); 27 | response.setSuccess({ 28 | content: content, 29 | }); 30 | } catch (error: unknown) { 31 | const nodeError = new GlobalError((error as Error).message); 32 | nodeError.setCode(500); 33 | response.setError(nodeError); 34 | } 35 | 36 | return response; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /triggers/http/src/nodes/examples/workflow-docs/InMemory.ts: -------------------------------------------------------------------------------- 1 | export default class InMemory { 2 | protected static _instance: InMemory; 3 | protected data: Record = {}; 4 | 5 | public static getInstance(): InMemory { 6 | if (!InMemory._instance) { 7 | InMemory._instance = new InMemory(); 8 | } 9 | return InMemory._instance; 10 | } 11 | 12 | get(key: string): string | null { 13 | return this.data[key] ?? null; 14 | } 15 | set(key: string, value: string): void { 16 | this.data[key] = value; 17 | } 18 | delete(key: string): void { 19 | delete this.data[key]; 20 | } 21 | clear(): void { 22 | this.data = {}; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /triggers/http/src/runner/MessageDecode.ts: -------------------------------------------------------------------------------- 1 | import type { JsonLikeObject } from "@nanoservice-ts/runner"; 2 | import type { Context } from "@nanoservice-ts/shared"; 3 | import type WorkflowRequest from "./types/WorkflowRequest"; 4 | import type WorkflowResponse from "./types/WorkflowResponse"; 5 | 6 | export default class MessageDecode { 7 | requestDecode(request: WorkflowRequest): Context { 8 | let message: Context = {}; 9 | 10 | switch (request.Encoding) { 11 | case "BASE64": { 12 | const messageStr = Buffer.from(request.Message, "base64").toString("utf-8"); 13 | message = this.decodeType(messageStr, request.Type); 14 | break; 15 | } 16 | case "STRING": { 17 | message = this.decodeType(request.Message, request.Type); 18 | break; 19 | } 20 | default: 21 | throw new Error(`Unsupported encoding: ${request.Encoding}`); 22 | } 23 | 24 | return message; 25 | } 26 | 27 | responseDecode(response: WorkflowResponse): JsonLikeObject { 28 | let message: JsonLikeObject = {}; 29 | console.log("response", response); 30 | 31 | switch (response.Encoding) { 32 | case "BASE64": { 33 | const messageStr = Buffer.from(response.Message, "base64").toString("utf-8"); 34 | message = this.decodeType(messageStr, response.Type) as unknown as JsonLikeObject; 35 | break; 36 | } 37 | case "STRING": { 38 | message = this.decodeType(response.Message, response.Type) as unknown as JsonLikeObject; 39 | break; 40 | } 41 | default: 42 | throw new Error(`Unsupported encoding: ${response.Encoding}`); 43 | } 44 | 45 | return message; 46 | } 47 | 48 | decodeType(message: string, type: string): Context { 49 | switch (type) { 50 | case "JSON": { 51 | return JSON.parse(message); 52 | } 53 | default: 54 | throw new Error(`Unsupported type: ${type}`); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /triggers/http/src/runner/metrics/opentelemetry_metrics.ts: -------------------------------------------------------------------------------- 1 | import { DefaultLogger } from "@nanoservice-ts/runner"; 2 | import { metrics } from "@opentelemetry/api"; 3 | import { PrometheusExporter } from "@opentelemetry/exporter-prometheus"; 4 | import { Resource } from "@opentelemetry/resources"; 5 | import { MeterProvider } from "@opentelemetry/sdk-metrics"; 6 | import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions"; 7 | 8 | // Set up the Prometheus exporter to expose metrics at /metrics on port 9091 9 | const prometheusExporter = new PrometheusExporter({}, () => 10 | new DefaultLogger().log("Metrics endpoint: http://localhost:4000/metrics"), 11 | ); 12 | 13 | const resource = Resource.default().merge( 14 | new Resource({ 15 | [ATTR_SERVICE_NAME]: "trigger-http", 16 | [ATTR_SERVICE_VERSION]: "0.0.8", 17 | }), 18 | ); 19 | 20 | // Creates MeterProvider and installs the exporter as a MetricReader 21 | const meterProvider = new MeterProvider({ 22 | resource: resource, 23 | }); 24 | meterProvider.addMetricReader(prometheusExporter); 25 | 26 | metrics.setGlobalMeterProvider(meterProvider); 27 | 28 | const meter = metrics.getMeter("default"); 29 | const metricsHandler = prometheusExporter.getMetricsRequestHandler.bind(prometheusExporter); 30 | 31 | export { meter, metricsHandler }; 32 | -------------------------------------------------------------------------------- /triggers/http/src/runner/metrics/opentelemetry_traces.ts: -------------------------------------------------------------------------------- 1 | // import { DiagConsoleLogger, DiagLogLevel, diag } from "@opentelemetry/api"; 2 | import { Resource } from "@opentelemetry/resources"; 3 | import { 4 | BatchSpanProcessor, 5 | ConsoleSpanExporter, 6 | type SpanExporter, 7 | type SpanProcessor, 8 | } from "@opentelemetry/sdk-trace-base"; 9 | import { WebTracerProvider } from "@opentelemetry/sdk-trace-web"; 10 | import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions"; 11 | 12 | // For troubleshooting, set the log level to DiagLogLevel.DEBUG 13 | // diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ALL); 14 | 15 | const resource = Resource.default().merge( 16 | new Resource({ 17 | [ATTR_SERVICE_NAME]: "trigger-http", 18 | [ATTR_SERVICE_VERSION]: "0.0.8", 19 | }), 20 | ); 21 | 22 | const exporter: SpanExporter = new ConsoleSpanExporter(); 23 | const processor: SpanProcessor = new BatchSpanProcessor(exporter); 24 | 25 | const provider = new WebTracerProvider({ 26 | resource: resource, 27 | spanProcessors: [processor], 28 | }); 29 | 30 | provider.register(); 31 | -------------------------------------------------------------------------------- /triggers/http/src/runner/types/ErrorResponse.ts: -------------------------------------------------------------------------------- 1 | type ErrorResponse = { 2 | error: string; 3 | }; 4 | -------------------------------------------------------------------------------- /triggers/http/src/runner/types/NodeTypes.ts: -------------------------------------------------------------------------------- 1 | enum NodeTypes { 2 | MODULE = "module", 3 | LOCAL = "local", 4 | PYTHON3 = "runtime.python3", 5 | } 6 | 7 | export default NodeTypes; 8 | -------------------------------------------------------------------------------- /triggers/http/src/runner/types/Nodes.ts: -------------------------------------------------------------------------------- 1 | import type { NodeBase } from "@nanoservice-ts/shared"; 2 | 3 | type Nodes = { 4 | [key: string]: NodeBase; 5 | }; 6 | 7 | export default Nodes; 8 | -------------------------------------------------------------------------------- /triggers/http/src/runner/types/RuntimeWorkflow.ts: -------------------------------------------------------------------------------- 1 | import type { ParamsDictionary } from "@nanoservice-ts/runner"; 2 | 3 | type RuntimeWorkflow = { 4 | workflow: ParamsDictionary; 5 | }; 6 | 7 | export default RuntimeWorkflow; 8 | -------------------------------------------------------------------------------- /triggers/http/src/runner/types/WorkflowRequest.ts: -------------------------------------------------------------------------------- 1 | type WorkflowRequest = { 2 | Name: string; 3 | Encoding: string; 4 | Message: string; 5 | Type: string; 6 | }; 7 | 8 | export default WorkflowRequest; 9 | -------------------------------------------------------------------------------- /triggers/http/src/runner/types/WorkflowResponse.ts: -------------------------------------------------------------------------------- 1 | type WorkflowResponse = { 2 | Encoding: string; 3 | Message: string; 4 | Type: string; 5 | }; 6 | 7 | export default WorkflowResponse; 8 | -------------------------------------------------------------------------------- /triggers/http/src/runner/types/Workflows.ts: -------------------------------------------------------------------------------- 1 | import type { HelperResponse } from "@nanoservice-ts/helper"; 2 | 3 | type Workflows = { 4 | [key: string]: HelperResponse; 5 | }; 6 | 7 | export default Workflows; 8 | -------------------------------------------------------------------------------- /triggers/http/src/workflows/countries-cats-helper.ts: -------------------------------------------------------------------------------- 1 | import { AddElse, AddIf, type Step, Workflow } from "@nanoservice-ts/helper"; 2 | 3 | const step: Step = Workflow({ 4 | name: "World Countries", 5 | version: "1.0.0", 6 | description: "Workflow description", 7 | }) 8 | .addTrigger("http", { 9 | method: "GET", 10 | path: "/", 11 | accept: "application/json", 12 | }) 13 | .addCondition({ 14 | node: { 15 | name: "filter-request", 16 | node: "@nanoservice-ts/if-else", 17 | type: "module", 18 | }, 19 | conditions: () => { 20 | return [ 21 | new AddIf('ctx.request.query.countries === "true"') 22 | .addStep({ 23 | name: "get-countries", 24 | node: "@nanoservice-ts/api-call", 25 | type: "module", 26 | inputs: { 27 | url: "https://countriesnow.space/api/v0.1/countries", 28 | method: "GET", 29 | headers: { 30 | "Content-Type": "application/json", 31 | }, 32 | responseType: "application/json", 33 | }, 34 | }) 35 | .build(), 36 | new AddElse() 37 | .addStep({ 38 | name: "get-facts", 39 | node: "@nanoservice-ts/api-call", 40 | type: "module", 41 | inputs: { 42 | url: "https://catfact.ninja/fact", 43 | method: "GET", 44 | headers: { 45 | "Content-Type": "application/json", 46 | }, 47 | responseType: "application/json", 48 | }, 49 | }) 50 | .build(), 51 | ]; 52 | }, 53 | }); 54 | 55 | export default step; 56 | -------------------------------------------------------------------------------- /triggers/http/src/workflows/countries-helper.ts: -------------------------------------------------------------------------------- 1 | import { type Step, Workflow } from "@nanoservice-ts/helper"; 2 | 3 | const step1Inputs = { 4 | url: "https://countriesnow.space/api/v0.1/countries/capital", 5 | method: "GET", 6 | headers: { 7 | "Content-Type": "application/json", 8 | }, 9 | responseType: "application/json", 10 | }; 11 | 12 | const step: Step = Workflow({ 13 | name: "World Countries", 14 | version: "1.0.0", 15 | description: "Workflow description", 16 | }) 17 | .addTrigger("http", { 18 | method: "GET", 19 | path: "/", 20 | accept: "application/json", 21 | }) 22 | .addStep({ 23 | name: "get-countries-api", 24 | node: "@nanoservice-ts/api-call", 25 | type: "module", 26 | inputs: step1Inputs, 27 | }); 28 | 29 | export default step; 30 | -------------------------------------------------------------------------------- /triggers/http/src/workflows/empty.ts: -------------------------------------------------------------------------------- 1 | import { AddElse, AddIf, type Step, Workflow } from "@nanoservice-ts/helper"; 2 | 3 | const step: Step = Workflow({ 4 | name: "Empty", 5 | version: "1.0.0", 6 | description: "Workflow for load testing", 7 | }) 8 | .addTrigger("http", { 9 | method: "GET", 10 | path: "/", 11 | accept: "application/json", 12 | }) 13 | .addCondition({ 14 | node: { 15 | name: "filter-request", 16 | node: "@nanoservice-ts/if-else", 17 | type: "module", 18 | }, 19 | conditions: () => { 20 | return [new AddIf('ctx.request.query.countries === "true"').build(), new AddElse().build()]; 21 | }, 22 | }); 23 | 24 | export default step; 25 | -------------------------------------------------------------------------------- /triggers/http/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "CommonJS", 5 | "rootDir": "./src", 6 | "baseUrl": ".", 7 | "paths": { 8 | "@nodes/*": ["./src/nodes/*"], 9 | "@src/*": ["src/*"] 10 | }, 11 | "allowJs": true, 12 | "declaration": true, 13 | "sourceMap": true, 14 | "outDir": "./dist", 15 | "esModuleInterop": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noImplicitReturns": true, 20 | "skipLibCheck": true 21 | }, 22 | "compileOnSave": true, 23 | "include": [ 24 | "./src", 25 | "./src/nodes/**/*.json", 26 | "./src/nodes/**/*.md", 27 | "./src/*.json", 28 | "./src/nodes/**/*.jsx" 29 | ], 30 | "exclude": [ 31 | "src/nodes/**/test/*.ts", 32 | "node_modules", 33 | "src/nodes/examples/**/**" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /triggers/http/workflows/json/countries-py.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "World Countries", 3 | "description": "Workflow description", 4 | "version": "1.0.0", 5 | "trigger": { 6 | "http": { 7 | "method": "GET", 8 | "path": "/", 9 | "accept": "application/json" 10 | } 11 | }, 12 | "steps": [ 13 | { 14 | "name": "get-countries-api", 15 | "node": "api_call", 16 | "type": "runtime.python3" 17 | } 18 | ], 19 | "nodes": { 20 | "get-countries-api": { 21 | "inputs": { 22 | "url": "https://countriesnow.space/api/v0.1/countries/capital", 23 | "method": "GET", 24 | "headers": { 25 | "Content-Type": "application/json" 26 | }, 27 | "responseType": "application/json" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /triggers/http/workflows/json/countries-vs-facts.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Movies or Countries", 3 | "description": "Workflow description", 4 | "version": "1.0.0", 5 | "trigger": { 6 | "http": { 7 | "method": "GET", 8 | "path": "/", 9 | "accept": "application/json" 10 | } 11 | }, 12 | "steps": [ 13 | { 14 | "name": "filter-request", 15 | "node": "@nanoservice-ts/if-else", 16 | "type": "module" 17 | } 18 | ], 19 | "nodes": { 20 | "filter-request": { 21 | "conditions": [ 22 | { 23 | "type": "if", 24 | "steps": [ 25 | { 26 | "name": "get-countries", 27 | "node": "@nanoservice-ts/api-call", 28 | "type": "module" 29 | } 30 | ], 31 | "condition": "ctx.request.query.countries === \"true\"" 32 | }, 33 | { 34 | "type": "else", 35 | "steps": [ 36 | { 37 | "name": "get-facts", 38 | "node": "@nanoservice-ts/api-call", 39 | "type": "module" 40 | } 41 | ] 42 | } 43 | ] 44 | }, 45 | "get-facts": { 46 | "inputs": { 47 | "url": "https://catfact.ninja/fact", 48 | "method": "GET", 49 | "headers": { 50 | "Content-Type": "application/json" 51 | }, 52 | "responseType": "application/json" 53 | } 54 | }, 55 | "get-countries": { 56 | "inputs": { 57 | "url": "https://countriesnow.space/api/v0.1/countries/capital", 58 | "method": "GET", 59 | "headers": { 60 | "Content-Type": "application/json" 61 | }, 62 | "responseType": "application/json" 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /triggers/http/workflows/json/countries.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "World Countries", 3 | "description": "Workflow description", 4 | "version": "1.0.0", 5 | "trigger": { 6 | "http": { 7 | "method": "GET", 8 | "path": "/", 9 | "accept": "application/json" 10 | } 11 | }, 12 | "steps": [ 13 | { 14 | "name": "get-countries-api", 15 | "node": "@nanoservice-ts/api-call", 16 | "type": "module" 17 | } 18 | ], 19 | "nodes": { 20 | "get-countries-api": { 21 | "inputs": { 22 | "url": "https://countriesnow.space/api/v0.1/countries/capital", 23 | "method": "GET", 24 | "headers": { 25 | "Content-Type": "application/json" 26 | }, 27 | "responseType": "application/json" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /triggers/http/workflows/json/empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Empty", 3 | "description": "Load test empty workflow", 4 | "version": "1.0.0", 5 | "trigger": { 6 | "http": { 7 | "method": "GET", 8 | "path": "/", 9 | "accept": "application/json" 10 | } 11 | }, 12 | "steps": [], 13 | "nodes": {} 14 | } 15 | -------------------------------------------------------------------------------- /triggers/http/workflows/json/films.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Films Workflow", 3 | "description": "This workflow returns a list of films from Postgres", 4 | "version": "1.0.0", 5 | "trigger": { 6 | "http": { 7 | "method": "GET", 8 | "path": "/", 9 | "accept": "application/json" 10 | } 11 | }, 12 | "steps": [ 13 | { 14 | "name": "get-films", 15 | "node": "postgres-query", 16 | "type": "module" 17 | } 18 | ], 19 | "nodes": { 20 | "get-films": { 21 | "inputs": { 22 | "host": "localhost", 23 | "port": 5432, 24 | "database": "dvdrental", 25 | "user": "postgres", 26 | "password": "example", 27 | "query": "SELECT * FROM \"film\" ORDER BY \"title\" LIMIT 50" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /triggers/http/workflows/json/launches-by-year.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "launches-by-year", 3 | "description": "Get launches by year", 4 | "version": "1.0.0", 5 | "trigger": { 6 | "http": { 7 | "method": "GET", 8 | "path": "/", 9 | "accept": "application/json" 10 | } 11 | }, 12 | "steps": [ 13 | { 14 | "name": "api", 15 | "node": "@nanoservice-ts/api-call", 16 | "type": "module" 17 | } 18 | ], 19 | "nodes": { 20 | "api": { 21 | "inputs": { 22 | "url": "https://ll.thespacedevs.com/2.3.0/launches/?format=json&year=${ctx.request.query.dob ? new Date(ctx.request.query.dob).getYear() : new Date('2000-01-01').getYear()}", 23 | "method": "GET", 24 | "headers": { 25 | "Content-Type": "application/json" 26 | }, 27 | "responseType": "application/json" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /triggers/http/workflows/json/loadtest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Load test workflow", 3 | "description": "This workflow is used to test the load on the runner.", 4 | "version": "1.0.0", 5 | "trigger": { 6 | "http": { 7 | "method": "GET", 8 | "path": "/", 9 | "accept": "application/json" 10 | } 11 | }, 12 | "steps": [ 13 | { 14 | "name": "mapper", 15 | "node": "mapper", 16 | "type": "module" 17 | } 18 | ], 19 | "nodes": { 20 | "mapper": { 21 | "inputs": { 22 | "model": { 23 | "success": true 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /triggers/http/workflows/json/mongodb.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MongoDB CRUD API", 3 | "description": "Provides a RESTful API to interact dynamically with MongoDB collections.", 4 | "version": "1.0.0", 5 | "trigger": { 6 | "http": { 7 | "method": "*", 8 | "path": "/:collection/:id?", 9 | "accept": "application/json" 10 | } 11 | }, 12 | "steps": [ 13 | { 14 | "name": "mongodb_query", 15 | "node": "mongodb-query", 16 | "type": "module" 17 | } 18 | ], 19 | "nodes": { 20 | "mongodb_query": { 21 | "inputs": { 22 | "collection": "${ctx.request.params.collection}", 23 | "data": "js/ctx.request.body", 24 | "id": "${ctx.request.params.id}" 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /triggers/http/workflows/toml/countries.toml: -------------------------------------------------------------------------------- 1 | name = "World Countries" 2 | description = "Workflow description" 3 | version = "1.0.0" 4 | 5 | [trigger.http] 6 | method = "GET" 7 | path = "/" 8 | accept = "application/json" 9 | 10 | [[steps]] 11 | name = "get-countries-api" 12 | node = "@nanoservice-ts/api-call" 13 | type = "module" 14 | 15 | [nodes.get-countries-api.inputs] 16 | url = "https://countriesnow.space/api/v0.1/countries/capital" 17 | method = "GET" 18 | responseType = "application/json" 19 | 20 | [nodes.get-countries-api.inputs.headers] 21 | Content-Type = "application/json" 22 | -------------------------------------------------------------------------------- /triggers/http/workflows/xml/countries.xml: -------------------------------------------------------------------------------- 1 | World Countries 2 | Workflow description 3 | 1.0.0 4 | 5 | 6 | GET 7 | / 8 | application/json 9 | 10 | 11 | 12 | get-countries-api 13 | @nanoservice-ts/api-call 14 | module 15 | 16 | 17 | 18 | 19 | https://countriesnow.space/api/v0.1/countries/capital 20 | GET 21 | 22 | application/json 23 | 24 | application/json 25 | 26 | 27 | -------------------------------------------------------------------------------- /triggers/http/workflows/yaml/countries.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: World Countries 3 | description: Workflow description 4 | version: 1.0.0 5 | trigger: 6 | http: 7 | method: GET 8 | path: "/" 9 | accept: application/json 10 | steps: 11 | - name: get-countries-api 12 | node: "@nanoservice-ts/api-call" 13 | type: module 14 | nodes: 15 | get-countries-api: 16 | inputs: 17 | url: https://countriesnow.space/api/v0.1/countries/capital 18 | method: GET 19 | headers: 20 | Content-Type: application/json 21 | responseType: application/json 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "rootDir": "./", 6 | "declaration": true, 7 | "sourceMap": true, 8 | "outDir": "./dist", 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "noUnusedLocals": true, 13 | "noImplicitReturns": true, 14 | "skipLibCheck": true 15 | }, 16 | "compileOnSave": true, 17 | "typedocOptions": { 18 | "includeVersion": true, 19 | "readme": "none", 20 | "entryPoints": ["./core/runner/src/index.ts", "./core/shared/src/index.ts"], 21 | "plugin": ["typedoc-plugin-markdown"], 22 | "out": "./docs/ref" 23 | }, 24 | "include": ["./core/**/*"], 25 | "exclude": ["node_modules", "README.md", "LICENSE"] 26 | } 27 | -------------------------------------------------------------------------------- /workflows/json/countries-vs-facts.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Movies or Countries", 3 | "description": "Workflow description", 4 | "version": "1.0.0", 5 | "trigger": { 6 | "http": { 7 | "method": "GET", 8 | "path": "/", 9 | "accept": "application/json" 10 | } 11 | }, 12 | "steps": [ 13 | { 14 | "name": "filter-request", 15 | "node": "control-flow/if-else@1.0.0", 16 | "type": "local" 17 | } 18 | ], 19 | "nodes": { 20 | "filter-request": { 21 | "conditions": [ 22 | { 23 | "type": "if", 24 | "steps": [ 25 | { 26 | "name": "get-countries", 27 | "node": "@nanoservice-ts/api-call", 28 | "type": "module" 29 | } 30 | ], 31 | "condition": "ctx.request.query.countries === \"true\"" 32 | }, 33 | { 34 | "type": "else", 35 | "steps": [ 36 | { 37 | "name": "get-facts", 38 | "node": "@nanoservice-ts/api-call", 39 | "type": "module" 40 | } 41 | ] 42 | } 43 | ] 44 | }, 45 | "get-facts": { 46 | "inputs": { 47 | "url": "https://catfact.ninja/fact", 48 | "method": "GET", 49 | "headers": { 50 | "Content-Type": "application/json" 51 | }, 52 | "responseType": "application/json" 53 | } 54 | }, 55 | "get-countries": { 56 | "inputs": { 57 | "url": "https://countriesnow.space/api/v0.1/countries/capital", 58 | "method": "GET", 59 | "headers": { 60 | "Content-Type": "application/json" 61 | }, 62 | "responseType": "application/json" 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /workflows/json/countries.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "World Countries", 3 | "description": "Workflow description", 4 | "version": "1.0.0", 5 | "trigger": { 6 | "http": { 7 | "method": "GET", 8 | "path": "/", 9 | "accept": "application/json" 10 | } 11 | }, 12 | "steps": [ 13 | { 14 | "name": "get-countries-api", 15 | "node": "@nanoservice-ts/api-call", 16 | "type": "module" 17 | } 18 | ], 19 | "nodes": { 20 | "get-countries-api": { 21 | "inputs": { 22 | "url": "https://countriesnow.space/api/v0.1/countries/capital", 23 | "method": "GET", 24 | "headers": { 25 | "Content-Type": "application/json" 26 | }, 27 | "responseType": "application/json" 28 | } 29 | } 30 | } 31 | } 32 | --------------------------------------------------------------------------------