Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sc-32565] Repo setup #1

Merged
merged 28 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8fcd039
Initial commit
nrutman Jan 12, 2024
e5fbc9a
base code
nrutman Jan 16, 2024
bb00ec7
update try/catch
nrutman Jan 18, 2024
668dbdb
add documentation
nrutman Jan 18, 2024
fb09b41
Fix error interface
nrutman Jan 18, 2024
0a4c5ce
Remove unnecessary image
nrutman Jan 18, 2024
ca00f8c
Add CI workflow
nrutman Jan 18, 2024
643c6b3
Add link to docs
nrutman Jan 18, 2024
99ac7ab
Update eslint config
nrutman Jan 19, 2024
9073c95
Update prettier config
nrutman Jan 19, 2024
02d4b9d
Add editor config
nrutman Jan 19, 2024
c3d9377
Add jest config
nrutman Jan 19, 2024
aa852bf
Update tsconfig
nrutman Jan 19, 2024
37e3033
Update license
nrutman Jan 19, 2024
11ab154
Add override to error class property
nrutman Jan 19, 2024
3df89cc
Update eslint, prettier, and add no-unused-imports
nrutman Jan 19, 2024
87565d7
Remove unused formatter-summary
nrutman Jan 19, 2024
14c341e
Update CI
nrutman Jan 19, 2024
8b102fc
Move lodash to non-dev dependency
nrutman Jan 19, 2024
e0f636d
Don't build the tests or mocks
nrutman Jan 19, 2024
8f7bf83
prettier
nrutman Jan 19, 2024
cfa343f
Revert ignoring tests since it negatively impacts eslint
nrutman Jan 19, 2024
26c699d
Add unused-imports/no-unused-imports rule
namoscato Jan 20, 2024
22b3882
Remove unused ts-node config
namoscato Jan 20, 2024
0dc789a
Commit some VS Code settings
namoscato Jan 20, 2024
f2fd669
Add trailing newlines
namoscato Jan 20, 2024
0880b53
Simplify middleware interface
nrutman Jan 23, 2024
6cb9481
Merge branch 'repo-setup' of https://github.com/Fieldguide/pipeline i…
nrutman Jan 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# https://editorconfig.org/
# https://prettier.io/docs/en/configuration.html#editorconfig

root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
48 changes: 48 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint", "unused-imports"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
],
env: {
node: true,
},
overrides: [
{
files: ["**/*.ts?(x)"],
parserOptions: {
project: ["./tsconfig.json"],
tsconfigRootDir: __dirname,
},
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier",
],
rules: {
"@typescript-eslint/no-shadow": "warn",
"@typescript-eslint/no-unsafe-argument": "warn",
"@typescript-eslint/no-unsafe-assignment": "warn",
"@typescript-eslint/no-unsafe-call": "warn",
"@typescript-eslint/no-unsafe-member-access": "warn",
"@typescript-eslint/no-unsafe-return": "warn",
"unused-imports/no-unused-imports": "error",
curly: "error",
},
},
{
files: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(test).[jt]s?(x)"],
rules: {
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/unbound-method": "off",
},
},
],
};
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: ci
on:
pull_request:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-and-typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "npm"
- name: Cache node_modules/.cache directory
uses: actions/cache@v4
with:
path: node_modules/.cache
key: node-modules-dot-cache-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}-${{ github.run_id }}
restore-keys: |
node-modules-dot-cache-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}-
- run: npm ci --prefer-offline --no-audit
- run: npm run prettier
- run: npx tsc --noEmit
- run: npm run test:ci
- run: npm run eslint
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Compiled code
build/

# Dependency directories
node_modules/

# TypeScript cache
*.tsbuildinfo

# npm cache directory
.npm

# eslint cache
.eslintcache
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
18
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.github/*.md
.vscode
build
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
7 changes: 7 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"recommendations": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}
7 changes: 7 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"cSpell.words": [
"fieldguide"
],
"typescript.tsdk": "node_modules/typescript/lib",
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Fieldguide

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
124 changes: 122 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,122 @@
# pipeline
A toolkit to easily build synchronous process pipelines in TypeScript/JavaScript
# Pipeline

A type-safe toolkit to easily compose synchronous process chains in TypeScript/JavaScript

## Table of Contents

- [Table of Contents](#table-of-contents)
- [Overview](#overview)
- [Installation](#installation)
- [Types](#types)
- [Builder](#builder)
- [Initializer](#initializer)
- [Stages](#stages)
- [Stage Arguments](#stage-arguments)
- [Stage Results](#stage-results)
- [Results Validator](#results-validator)
- [Middleware](#middleware)
- [Error Handling](#error-handling)
- [Example Use Cases](#example-use-cases)

## Overview

This library seeks to provide a structure to break down large multi-step process chains in order to make them composable and testable.

![Pipeline overview diagram](./docs/pipeline-overview.png)

## Installation

Install the package with your favorite package manager:

```
npm install @fieldguide/pipeline
```

## Types

There are three core types to consider when constructing and using a pipeline:

1. **Arguments** represent the data that gets passed into the pipeline at runtime
2. **Context** represents the internal data that is shared with each pipeline stage
3. **Results** represent the data that is output from a pipeline execution

It is common to see these types abbreviated as `A`, `C`, and `R` respectively.

## Builder

The [pipeline builder](./src/buildPipeline.ts) is a factory that accepts the individual pieces of a pipeline and constructs a single callable of the `Pipeline` type:

```typescript
export type Pipeline<A extends object, R extends object> = (
args: A,
) => Promise<R>;
```

Notice that the callable takes a single arguments object (`A`) and outputs a promise representing the results object (`R`). The execution context (`C`) is internal to the pipeline stages, so it is not represented in the pipeline callable constructed by the builder.

## Initializer

The **Initializer** is a method that takes in the pipeline's arguments and produces a Context object. The context is then passed as an argument into each stage.

> [!NOTE]
> The initializer should create the "empty state" of the context object. It is recommended to initialize empty arrays, sets, maps, and objects within the initializer that stages can add data to or manipulate later.

## Stages

**Stages** are the independent steps in the process chain. They are processed synchronously (one at a time, in order) until the end of the chain is reached.

### Stage Arguments

The following arguments are provided to a stage when it is executed:

| Argument | Description |
| ---------- | ------------------------------------------------------------------------------------------------------------------ |
| `context` | The execution context object setup by the initializer and manipulated by any previous stages |
| `metadata` | The name of the pipeline (helpful for logging/metrics) and the initial arguments provided to the pipeline callable |

### Stage Results

Each stage can return a partial results object for the pipeline. These results are collected and merged as each stage is processed. If a later stage specifies the same key in the results object as an earlier stage, the later stage's result will overwrite the earlier result.

## Results Validator

The **Results Validator** ensures that the pipeline has fulfilled the interface specified for its result. Since each stage returns a _partial_ result set, this method verifies that the entire result has been supplied.

## Middleware

If **Middleware** is specified, it will be run on the specified stage lifecycle event(s) for each stage in the pipeline.

| Stage Event | Description |
| ----------------- | ---------------------------------- |
| `onStageStart` | Runs before each stage is executed |
| `onStageComplete` | Runs after each stage is executed |

Middleware is specified as an object with middleware callbacks mapped to at least one of the above event keys. A middleware callback is provided the following attributes:

| Parameter | Description |
| -------------- | ----------------------------------------------------------------------------- |
| `context` | The pipeline execution context object |
| `metadata` | The pipeline's metadata (name and runtime arguments) |
| `results` | A read-only set of results returned by stages so far |
| `stageNames` | An array of the names of the methods that make up the current pipeline stages |
| `currentStage` | The name of the current pipeline stage |

See the [LogStageMiddlewareFactory](./src/middleware/logStageMiddlewareFactory.ts) for a simple middleware implementation. It is wrapped in a factory method so a log method can be properly injected.

```typescript
buildPipeline({
/* ... */
middleware: [logStageMiddlewareFactory(logger.log)],
/* ... */
});
```

## Error Handling

When an error occurs during pipeline execution, an instance of [PipelineError](./src/error/PipelineError.ts) is thrown.

In order to decorate or extend pipeline error handling, use a standard try/catch block around pipeline invocation.

## Example Use Cases

1. [Data Export Pipeline](./docs/cases/data-export-pipeline.md)
6 changes: 6 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
presets: [
["@babel/preset-env", { targets: { node: "current" } }],
"@babel/preset-typescript",
],
};
Loading