Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicola Bovolato committed Mar 29, 2024
0 parents commit d494db2
Show file tree
Hide file tree
Showing 263 changed files with 27,098 additions and 0 deletions.
17 changes: 17 additions & 0 deletions .github/actions/base/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Setup pnpm
description: setup pnpm
inputs:
node-version:
description: "node version"
required: true
default: 20
runs:
using: composite
steps:
- uses: pnpm/action-setup@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: "pnpm"
- run: pnpm install --frozen-lockfile
shell: bash
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: CI
on:
push:
branches:
- master
pull_request:
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/base
- run: pnpm build
- run: pnpm lint
- env:
CI: true
run: pnpm test
23 changes: 23 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Deploy docs
on:
push:
branches:
- master
permissions:
contents: read
pages: write
id-token: write
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
jobs:
deploy-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/base
- run: pnpm build --filter docs
- uses: actions/upload-pages-artifact@v1
with:
path: packages/docs/build
- uses: actions/deploy-pages@v4
41 changes: 41 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# Dependencies
node_modules
.pnp
.pnp.js

# Local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Testing
coverage

# Turbo
.turbo

# Vercel
.vercel

# Build Outputs
.next/
out/
build
dist


# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Misc
.DS_Store
*.pem


.vscode
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Basica

The Foundational Library of Modern Applications
16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "basica-monorepo",
"private": true,
"scripts": {
"build": "turbo build",
"lint": "turbo lint",
"test": "turbo test"
},
"devDependencies": {
"turbo": "latest"
},
"packageManager": "[email protected]",
"engines": {
"node": ">=18"
}
}
4 changes: 4 additions & 0 deletions packages/basica/config/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["@basica/eslint-config/base.json"],
};
48 changes: 48 additions & 0 deletions packages/basica/config/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@basica/config",
"version": "0.0.1",
"author": "Nicola Bovolato",
"license": "MIT",
"files": [
"dist"
],
"exports": {
".": {
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
}
}
},
"engines": {
"node": ">=18.0.0"
},
"scripts": {
"build": "tsup-node",
"test": "vitest run",
"test:ui": "vitest --ui",
"test:unit": "vitest run --dir test/unit",
"test:integration": "vitest run --dir test/integration",
"lint": "tsc --noEmit && eslint \"{src,test}/**/*.ts\""
},
"dependencies": {
"@sinclair/typebox": "^0.32.16",
"dotenv": "^16.4.1"
},
"devDependencies": {
"@basica/eslint-config": "workspace:*",
"@basica/typescript-config": "workspace:*",
"@basica/tsup-config": "workspace:*",
"@basica/vitest-config": "workspace:*",
"@types/node": "^20.11.17",
"@vitest/coverage-v8": "^1.2.2",
"eslint": "^8.56.0",
"tsup": "^8.0.2",
"typescript": "^5.3.3",
"vitest": "^1.2.2"
}
}
80 changes: 80 additions & 0 deletions packages/basica/config/src/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { ConfigProvider, ConfigSchema } from "./utils";
import { config as dotenv, DotenvConfigOptions } from "dotenv";

export type Config = {
dotenv?: DotenvConfigOptions;
interpolator?: string;
casing?: "upper" | "lower" | "maintain";
};

export const envProvider = (config?: Config) => {
const {
dotenv: dotenvConfig,
interpolator = "_",
casing = "upper",
} = config ?? {};

dotenv(dotenvConfig);

const get = (schema: ConfigSchema, prefixes: string[] = []) => {
const parsed: Record<string, unknown> = {};

for (const [key, val] of Object.entries(schema)) {
const casedKey =
casing == "upper"
? key.toUpperCase()
: casing == "lower"
? key.toLowerCase()
: key;
const envKey = [...prefixes, casedKey];
const envKeyStr = envKey.join(interpolator);

let value: unknown;

if (typeof val == "string") {
value = parseEnvValue(val, envKeyStr);
} else if (Array.isArray(val)) {
value = parseEnvValue(val[0], envKeyStr);
if (!value) {
value = get(val[1] as ConfigSchema, envKey);
}
} else {
value = get(schema[key] as ConfigSchema, envKey);
}

parsed[key] = value;
}

return parsed;
};

return {
get,
} satisfies ConfigProvider;
};

const parseEnvValue = (val: "json" | "primitive", envVar: string) => {
const envValue = process.env[envVar];

if (val == "primitive") {
return envValue;
} else if (val == "json") {
if (envValue == undefined) {
// Skip if value is not set
return;
}

let value: string | undefined;
try {
value = JSON.parse(envValue);
} catch {
// Value is not a JSON object/array
try {
value = JSON.parse(JSON.stringify(envValue));
} catch {
/* empty */
}
}
return value;
}
};
35 changes: 35 additions & 0 deletions packages/basica/config/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Static, TObject } from "@sinclair/typebox";
import { Value } from "@sinclair/typebox/value";

import { ConfigProvider, schemaToObj } from "./utils";

export const configure = <T extends TObject>(
provider: ConfigProvider,
schema: T
) => {
const objSchema = schemaToObj(schema);
const config = provider.get(objSchema);

const result = safeParse(schema, config);

if (!result.success) {
const msg = [...result.errors]
.map((e) => e.path + ": " + e.message)
.join("\n");
throw new Error(msg);
}

return result.value as Static<typeof schema>;
};

const safeParse = <T extends TObject>(T: T, value: unknown) => {
const coerced = Value.Clean(T, Value.Default(T, Value.Convert(T, value)));

try {
return { value: Value.Decode(T, coerced), success: true } as const;
} catch (e) {
return { errors: Value.Errors(T, coerced), success: false } as const;
}
};

export { envProvider } from "./env";
Loading

0 comments on commit d494db2

Please sign in to comment.