Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ryota-ka committed Feb 10, 2022
0 parents commit 0681382
Show file tree
Hide file tree
Showing 13 changed files with 1,739 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const path = require('path');

module.exports = {
env: {
browser: true,
},
extends: ['@herp-inc', 'prettier'],
parserOptions: {
project: path.join(__dirname, 'tsconfig.json'),
},
};
30 changes: 30 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Build
on:
push:
branches:
- '**'
tags-ignore:
- '**'
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: 'lts/*'
- name: Install dependencies
run: |
yarn install --frozen-lockfile
- name: Run Prettier
run: |
yarn prettier --check --ignore-unknown .
- name: Typecheck
run: |
yarn tsc --noEmit
- name: Run ESLint
run: |
yarn eslint ./src
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/node_modules/
/result
14 changes: 14 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"printWidth": 120,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"overrides": [
{
"files": ["*.js", "*.ts", "*.tsx"],
"options": {
"tabWidth": 4
}
}
]
}
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
all: sdist

format: install
yarn prettier --write .

install:
yarn install

lint: install
yarn eslint ./src

sdist:
nix-build

typecheck: install
yarn tsc --noEmit --watch
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# `@herp-inc/cycle-launchdarkly-driver` [![npm](https://img.shields.io/npm/v/@herp-inc/cycle-launchdarkly-driver)](https://www.npmjs.com/package/@herp-inc/cycle-launchdarkly-driver)

[LaunchDarkly](https://launchdarkly.com/) driver for [Cycle.js](https://cycle.js.org/), based on [fp-ts](https://gcanti.github.io/fp-ts/) and [io-ts](https://gcanti.github.io/fp-ts/)

## Installation

Note that the following packages are peer dependencies of this library, which need to be installed separately.

| Package | Version |
| ---------------------------------------------------------------------------------------- | ------- |
| [`fp-ts`](https://www.npmjs.com/package/fp-ts) | `^2.11` |
| [`io-ts`](https://www.npmjs.com/package/io-ts) | `^2.2` |
| [`launchdarkly-js-client-sdk`](https://www.npmjs.com/package/launchdarkly-js-client-sdk) | `2` |
| [`xstream`](https://www.npmjs.com/package/xstream) | `11` |

```sh
$ yarn add @herp-inc/cycle-launchdarkly-driver
```

## Example

```typescript
import * as t from 'io-ts/Decoder';

type Features = {
foo: boolean;
bar: number;
baz: string;
};

const Features = {
decoder: t.type({
foo: t.boolean,
bar: t.number,
baz: t.string,
}),
defaultValues: {
foo: false,
bar: 0,
baz: '',
},
};

function main({ features }) {
return {
DOM: features.stream.map(view),
};
}

const drivers = {
features: makeLaunchDarklyDriver({
envKey: YOUR_CLIENT_SIDE_ID,
decoder: FeatureFlags.decoder,
defaultValues: FeatureFlags.defaultValues,
options: {
bootstrap: 'localStorage',
},
user: {
key: user.id,
},
}),
};

run(main, drivers);
```
32 changes: 32 additions & 0 deletions default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{ pkgs ? import <nixpkgs> {} }:

let
packageJSON = pkgs.lib.importJSON ./package.json;
in

pkgs.stdenv.mkDerivation rec {
name = "herp-inc-cycle-launchdarkly-driver";
version = packageJSON.version;

src = pkgs.nix-gitignore.gitignoreSource [] ./.;

buildInputs = [
pkgs.nodejs-slim
pkgs.yarn
];

buildPhase=''
HOME=$TMP yarn install --frozen-lockfile
yarn rollup --config ./rollup.config.js
cp ./package.json ./README.md ./dist
cd ./dist
yarn pack
cd ..
mv ./dist/*.tgz ./
'';

dontInstall = true;

doDist = true;
tarballs = "herp-inc-cycle-launchdarkly-driver-v${version}.tgz";
}
37 changes: 37 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@herp-inc/cycle-launchdarkly-driver",
"description": "LaunchDarkly driver for Cycle.js, based on fp-ts and io-ts",
"version": "0.1.0",
"keywords": [
"Cycle.js",
"LaunchDarkly"
],
"main": "index.js",
"typings": "index.d.ts",
"sideEffects": false,
"repository": "https://github.com/herp-inc/cycle-launchdarkly-driver",
"license": "MIT",
"devDependencies": {
"@cycle/run": "5.7.0",
"@herp-inc/eslint-config": "0.12.0",
"@rollup/plugin-typescript": "8.3.0",
"@typescript-eslint/eslint-plugin": "5.11.0",
"@typescript-eslint/parser": "5.11.0",
"eslint": "8.8.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-import": "2.25.4",
"fp-ts": "2.11.8",
"io-ts": "2.2.16",
"launchdarkly-js-client-sdk": "2.20.0",
"prettier": "2.5.1",
"rollup": "2.67.2",
"typescript": "4.5.5",
"xstream": "11.14.0"
},
"peerDependencies": {
"fp-ts": "^2.11",
"io-ts": "^2.2",
"launchdarkly-js-client-sdk": "2",
"xstream": "11"
}
}
3 changes: 3 additions & 0 deletions renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["github>herp-inc/renovate.json"]
}
14 changes: 14 additions & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import typescript from '@rollup/plugin-typescript';

export default {
input: 'src/index.ts',
output: {
dir: 'dist',
format: 'esm',
},
plugins: [
typescript({
outDir: 'dist',
}),
],
};
94 changes: 94 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import type { Driver } from '@cycle/run';
import * as E from 'fp-ts/Either';
import { pipe } from 'fp-ts/function';
import type { Decoder } from 'io-ts/Decoder';
import * as LaunchDarkly from 'launchdarkly-js-client-sdk';
import { type MemoryStream, Stream } from 'xstream';

type Dictionary = Readonly<{ [_: string]: unknown }>;

export interface FeaturesSource<Features extends Dictionary> {
readonly stream: MemoryStream<Features>;
}

type LDParams = Parameters<typeof LaunchDarkly.initialize>;

export type Params<Features extends Dictionary> = Readonly<{
/**
* The decoder for the feature flags.
*/
decoder: Decoder<unknown, Features>;
/**
* The default values of the feature flags.
*/
defaultValues: Features;
/**
* The client-side ID.
*/
envKey: LDParams[0];
/**
* Optional configuration settings.
*/
options?: LDParams[2];
/**
* The initial user properties.
*/
user: LDParams[1];
}>;

/**
* A factory function for the LaunchDarkly driver.
*/
export function makeLaunchDarklyDriver<Features extends Dictionary>({
decoder,
defaultValues,
envKey,
options,
user,
}: Params<Features>): Driver<void, FeaturesSource<Features>> {
let client: LaunchDarkly.LDClient;

return () => ({
stream: Stream.create<Features>({
start(listener) {
const emit = (): void => {
const allFlags = client.allFlags();
const flags = decoder.decode(allFlags);

const action = pipe(
flags,
E.fold(
() => () =>
options?.logger?.warn(`Failed to decode the flags: ${JSON.stringify(allFlags)}`),
(flags) => () => {
listener.next(flags);
},
),
);

action();
};

client = LaunchDarkly.initialize(envKey, user, options);

client.on('change', emit);
client.on('ready', emit);

client.on('error', listener.error);
client.on('failed', listener.error);
},
async stop() {
await client?.close();
},
}).startWith(defaultValues),
});
}

/**
* A factory function to create a mocked `FeaturesSource`, for testing purposes.
*/
export function makeMockFeaturesDriver<Features extends Dictionary>(
$: Stream<Features>,
): Driver<void, FeaturesSource<Features>> {
return () => ({ stream: $ });
}
16 changes: 16 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"strict": true,
"target": "es2021"
},
"include": ["src/**/*"]
}
Loading

0 comments on commit 0681382

Please sign in to comment.