├── .changeset ├── README.md └── config.json ├── .devcontainer ├── Dockerfile ├── devcontainer.json ├── humanlog.sh └── usepnpm.sh ├── .github ├── FUNDING.yml └── workflows │ └── release.yaml ├── .gitignore ├── .ncurc.json ├── .npmrc ├── .nvmrc ├── .vscode ├── extensions.json ├── launch.json ├── model.code-snippets ├── service.code-snippets ├── settings.json └── tasks.json ├── README.md ├── docs └── bag-of-tricks.md ├── eslint.base.config.mjs ├── eslint.vue.config.mjs ├── package.json ├── packages ├── cli │ ├── .gitignore │ ├── .madgerc │ ├── .npmignore │ ├── CHANGELOG.md │ ├── bin.js │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── link.ts │ │ ├── shared.ts │ │ ├── sync.ts │ │ └── unlink.ts │ ├── tsconfig.json │ ├── vitest.config.ts │ └── wallaby.cjs ├── effect-app │ ├── .madgerc │ ├── .npmignore │ ├── CHANGELOG.md │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── Array.ts │ │ ├── Chunk.ts │ │ ├── Config │ │ │ ├── SecretURL.ts │ │ │ └── internal │ │ │ │ └── configSecretURL.ts │ │ ├── Context.ts │ │ ├── Effect.ts │ │ ├── Function.ts │ │ ├── Inputify.type.ts │ │ ├── NonEmptySet.ts │ │ ├── NonEmptySetAspects.ts │ │ ├── NonEmptySetBase.ts │ │ ├── Operations.ts │ │ ├── Option.ts │ │ ├── Pure.ts │ │ ├── Schema.ts │ │ ├── Schema │ │ │ ├── Class.ts │ │ │ ├── FastCheck.ts │ │ │ ├── Methods.ts │ │ │ ├── brand.ts │ │ │ ├── email.ts │ │ │ ├── ext.ts │ │ │ ├── moreStrings.ts │ │ │ ├── numbers.ts │ │ │ ├── phoneNumber.ts │ │ │ ├── schema.ts │ │ │ └── strings.ts │ │ ├── Set.ts │ │ ├── Struct.ts │ │ ├── Tag.ts │ │ ├── Types.ts │ │ ├── Unify.ts │ │ ├── Widen.type.ts │ │ ├── _ext │ │ │ ├── Array.ts │ │ │ ├── date.ts │ │ │ ├── misc.ts │ │ │ └── ord.ext.ts │ │ ├── builtin.ts │ │ ├── client.ts │ │ ├── client │ │ │ ├── apiClientFactory.ts │ │ │ ├── clientFor.ts │ │ │ ├── errors.ts │ │ │ └── req.ts │ │ ├── faker.ts │ │ ├── http.ts │ │ ├── http │ │ │ ├── Request.ts │ │ │ └── internal │ │ │ │ └── lib.ts │ │ ├── ids.ts │ │ ├── index.ts │ │ ├── internal │ │ │ ├── Prelude.d.ts │ │ │ ├── Prelude.d.ts.map │ │ │ ├── Prelude.js │ │ │ └── lib.ts │ │ ├── logger.ts │ │ ├── transform.ts │ │ ├── utils.ts │ │ ├── utils │ │ │ ├── effectify.ts │ │ │ ├── extend.ts │ │ │ ├── logLevel.ts │ │ │ └── logger.ts │ │ ├── validation.ts │ │ └── validation │ │ │ └── validators.ts │ ├── test │ │ ├── rpc.test.ts │ │ ├── schema.test.ts │ │ └── utils.test.ts │ ├── tsconfig.base.json │ ├── tsconfig.json │ ├── tsconfig.src.json │ ├── tsconfig.test.json │ ├── vitest.config.ts │ └── wallaby.cjs ├── eslint-codegen-model │ ├── .gitignore │ ├── CHANGELOG.md │ ├── dist │ │ ├── compiler.d.ts │ │ ├── compiler.d.ts.map │ │ ├── compiler.js │ │ ├── index.d.ts │ │ ├── index.d.ts.map │ │ ├── index.js │ │ └── presets │ │ │ ├── barrel.d.ts │ │ │ ├── barrel.d.ts.map │ │ │ ├── barrel.js │ │ │ ├── index.d.ts │ │ │ ├── index.d.ts.map │ │ │ ├── index.js │ │ │ ├── meta.d.ts │ │ │ ├── meta.d.ts.map │ │ │ ├── meta.js │ │ │ ├── model.d.ts │ │ │ ├── model.d.ts.map │ │ │ └── model.js │ ├── eslint.config.mjs │ ├── index.js │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── presets │ │ │ ├── barrel.ts │ │ │ ├── index.ts │ │ │ ├── meta.ts │ │ │ └── model.ts │ └── tsconfig.json ├── infra │ ├── .madgerc │ ├── .npmignore │ ├── CHANGELOG.md │ ├── eslint.config.mjs │ ├── examples │ │ └── query.ts │ ├── package.json │ ├── src │ │ ├── CUPS.ts │ │ ├── Emailer.ts │ │ ├── Emailer │ │ │ ├── Sendgrid.ts │ │ │ ├── fake.ts │ │ │ └── service.ts │ │ ├── MainFiberSet.ts │ │ ├── Model.ts │ │ ├── Model │ │ │ ├── Repository.ts │ │ │ ├── Repository │ │ │ │ ├── ext.ts │ │ │ │ ├── internal │ │ │ │ │ └── internal.ts │ │ │ │ ├── legacy.ts │ │ │ │ ├── makeRepo.ts │ │ │ │ └── service.ts │ │ │ ├── dsl.ts │ │ │ ├── filter │ │ │ │ ├── filterApi.ts │ │ │ │ ├── types.ts │ │ │ │ └── types │ │ │ │ │ ├── errors.ts │ │ │ │ │ ├── fields.ts │ │ │ │ │ ├── path │ │ │ │ │ ├── common.ts │ │ │ │ │ ├── eager.ts │ │ │ │ │ └── index.ts │ │ │ │ │ ├── utils.ts │ │ │ │ │ └── validator.ts │ │ │ ├── query.ts │ │ │ └── query │ │ │ │ ├── dsl.ts │ │ │ │ └── new-kid-interpreter.ts │ │ ├── Operations.ts │ │ ├── OperationsRepo.ts │ │ ├── QueueMaker │ │ │ ├── SQLQueue.ts │ │ │ ├── errors.ts │ │ │ ├── memQueue.ts │ │ │ ├── sbqueue.ts │ │ │ └── service.ts │ │ ├── RequestContext.ts │ │ ├── RequestFiberSet.ts │ │ ├── Store.ts │ │ ├── Store │ │ │ ├── ContextMapContainer.ts │ │ │ ├── Cosmos.ts │ │ │ ├── Cosmos │ │ │ │ └── query.ts │ │ │ ├── Disk.ts │ │ │ ├── Memory.ts │ │ │ ├── codeFilter.ts │ │ │ ├── index.test.ts.bak │ │ │ ├── index.ts │ │ │ ├── service.ts │ │ │ └── utils.ts │ │ ├── adapters │ │ │ ├── SQL.ts │ │ │ ├── SQL │ │ │ │ └── Model.ts │ │ │ ├── ServiceBus.ts │ │ │ ├── cosmos-client.ts │ │ │ ├── index.ts │ │ │ ├── logger.ts │ │ │ ├── memQueue.ts │ │ │ ├── mongo-client.ts │ │ │ └── redis-client.ts │ │ ├── api │ │ │ ├── codec.ts │ │ │ ├── internal │ │ │ │ ├── RequestContextMiddleware.ts │ │ │ │ ├── auth.ts │ │ │ │ ├── events.ts │ │ │ │ ├── health.ts │ │ │ │ └── middlewares.ts │ │ │ ├── middlewares.ts │ │ │ ├── reportError.ts │ │ │ ├── routing.ts │ │ │ ├── routing │ │ │ │ ├── DynamicMiddleware.ts │ │ │ │ ├── schema │ │ │ │ │ └── jwt.ts │ │ │ │ └── utils.ts │ │ │ ├── setupRequest.ts │ │ │ └── util.ts │ │ ├── arbs.ts │ │ ├── errorReporter.ts │ │ ├── errors.ts │ │ ├── fileUtil.ts │ │ ├── index.ts │ │ ├── logger.ts │ │ ├── logger │ │ │ ├── jsonLogger.ts │ │ │ ├── logFmtLogger.ts │ │ │ └── shared.ts │ │ ├── rateLimit.ts │ │ ├── test.ts │ │ └── vitest.ts │ ├── test │ │ ├── controller.test.ts │ │ └── query.test.ts │ ├── tsconfig.examples.json │ ├── tsconfig.json │ ├── tsconfig.src.json │ ├── tsconfig.test.json │ ├── types │ │ └── modules.d.ts │ ├── vitest.config.ts │ └── wallaby.cjs ├── vue-components │ ├── .prettierrc │ ├── .storybook │ │ ├── main.ts │ │ └── preview.ts │ ├── .vscode │ │ └── extensions.json │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__ │ │ ├── OmegaForm.test.ts │ │ ├── OmegaForm │ │ │ └── OmegaAutoGen.test.ts │ │ ├── OmegaFormTest.vue │ │ ├── OmegaIntlProvider.vue │ │ └── tsconfig.json │ ├── env.d.ts │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── assets │ │ │ └── .keep │ │ ├── components │ │ │ ├── OmegaForm │ │ │ │ ├── InputProps.ts │ │ │ │ ├── OmegaAutoGen.vue │ │ │ │ ├── OmegaErrors.vue │ │ │ │ ├── OmegaErrorsContext.ts │ │ │ │ ├── OmegaFormInput.vue │ │ │ │ ├── OmegaFormStuff.ts │ │ │ │ ├── OmegaInput.vue │ │ │ │ ├── OmegaInputVuetify.vue │ │ │ │ ├── OmegaInternalInput.vue │ │ │ │ ├── OmegaWrapper.vue │ │ │ │ ├── getOmegaStore.ts │ │ │ │ ├── index.ts │ │ │ │ └── useOmegaForm.ts │ │ │ ├── index.ts │ │ │ └── style.css │ │ ├── constants │ │ │ └── index.ts │ │ ├── index.ts │ │ └── utils │ │ │ └── index.ts │ ├── stories │ │ ├── OmegaForm.stories.ts │ │ ├── OmegaForm │ │ │ ├── AutoGeneration.vue │ │ │ ├── Booleans.vue │ │ │ ├── Clearable.vue │ │ │ ├── ComplexForm.vue │ │ │ ├── EmailForm.vue │ │ │ ├── Meta.vue │ │ │ ├── OneHundredWaysToWriteAForm.vue │ │ │ ├── PersistencyForm.vue │ │ │ ├── SimpleForm.vue │ │ │ ├── SimpleFormVuetifyDefault.vue │ │ │ ├── SumExample.vue │ │ │ └── form.Input.vue │ │ ├── README.md │ │ └── tsconfig.json │ ├── tsconfig.json │ ├── vite.config.ts │ └── vitest.config.ts └── vue │ ├── .madgerc │ ├── .npmignore │ ├── .prettierignore │ ├── CHANGELOG.md │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ ├── errorReporter.ts │ ├── form.ts │ ├── index.ts │ ├── lib.ts │ ├── makeClient.ts │ ├── makeContext.ts │ ├── makeIntl.ts │ ├── mutate.ts │ ├── query.ts │ ├── routeParams.ts │ └── runtime.ts │ ├── test │ └── form.test.ts │ ├── tsconfig.json │ ├── tsconfig.src.json │ ├── tsconfig.test.json │ ├── vitest.config.ts │ └── wallaby.cjs ├── patches ├── .gitkeep ├── @tanstack__query-core.patch ├── effect.patch ├── eslint-plugin-codegen@0.17.0.patch ├── ts-plugin-sort-import-suggestions.patch └── typescript.patch ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── scripts ├── clean-dist.sh ├── convert_patch.js ├── extract.sh ├── mergeTsConfig.cjs └── sort.js ├── tsconfig.all.json ├── tsconfig.base.json ├── tsconfig.lib.json ├── tsconfig.plugins.json ├── vite.config.base.ts ├── vitest.workspace.ts └── wallaby.base.cjs /.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@2.2.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch" 10 | } -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Specifies which operating system image to use. 2 | FROM mcr.microsoft.com/vscode/devcontainers/base:focal 3 | 4 | WORKDIR /home/vscode 5 | 6 | # curl and ca-certificates are needed for volta installation 7 | RUN apt-get update \ 8 | && apt-get install -y \ 9 | curl \ 10 | ca-certificates \ 11 | --no-install-recommends 12 | 13 | # Changes user to vscode and the SHELL to bash 14 | USER vscode 15 | SHELL ["/bin/bash", "-c"] 16 | 17 | # since we're starting non-interactive shell, we wil need to tell bash to load .bashrc manually 18 | ENV BASH_ENV ~/.bashrc 19 | # needed by volta() function 20 | ENV VOLTA_HOME /home/vscode/.volta 21 | # make sure packages managed by volta will be in PATH 22 | ENV PATH $VOLTA_HOME/bin:$PATH 23 | 24 | # download volta 25 | RUN curl https://get.volta.sh | bash 26 | # change the working directory to the one where the project lives 27 | WORKDIR /workspaces/macs-configurator 28 | 29 | # and install node and pnpm 30 | RUN volta install node@22 31 | RUN npm i -g pnpm 32 | 33 | RUN echo 34 | 35 | ENV SHELL=/bin/bash 36 | RUN curl -L "https://humanlog.io/install.sh" | sh 37 | COPY humanlog.sh /tmp/ 38 | RUN cat /tmp/humanlog.sh >> ~/.bashrc 39 | 40 | RUN echo 41 | 42 | # prevent `npm or yarn` usage in the shell now that pnpm is installed 43 | WORKDIR /home/vscode 44 | COPY usepnpm.sh /tmp/ 45 | RUN cat /tmp/usepnpm.sh >> ~/.bashrc -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Effect App Libs Codespace", 3 | "build": { 4 | "dockerfile": "Dockerfile" 5 | }, 6 | // see https://containers.dev/implementors/json_reference/#remoteUser 7 | // you can change the user name to whatever you want, this is used to avoid installing stuff in the image as root 8 | "remoteUser": "vscode", 9 | "postCreateCommand": "pnpm i && pnpm build", 10 | "hostRequirements": { 11 | "cpus": 4, 12 | "memory": "8gb" 13 | } 14 | } -------------------------------------------------------------------------------- /.devcontainer/humanlog.sh: -------------------------------------------------------------------------------- 1 | export HUMANLOG_INSTALL="/home/vscode/.humanlog" 2 | export PATH="$HUMANLOG_INSTALL/bin:$PATH" -------------------------------------------------------------------------------- /.devcontainer/usepnpm.sh: -------------------------------------------------------------------------------- 1 | NPM_PATH=$(which npm) 2 | npm () { 3 | if [ -e pnpm-lock.yaml ] 4 | then 5 | echo "Please use PNPM with this project" 6 | elif [ -e yarn.lock ] 7 | then 8 | echo "Please use Yarn with this project" 9 | else 10 | $NPM_PATH "$@" 11 | fi 12 | } 13 | 14 | 15 | YARN_PATH=$(which yarn) 16 | yarn () { 17 | if [ -e pnpm-lock.yaml ] 18 | then 19 | echo "Please use PNPM with this project" 20 | elif [ -e package-lock.json ] 21 | then 22 | echo "Please use NPM with this project" 23 | else 24 | $YARN_PATH "$@" 25 | fi 26 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [patroza] 4 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Changesets 2 | on: 3 | push: 4 | branches: 5 | - main 6 | env: 7 | CI: true 8 | PNPM_CACHE_FOLDER: .pnpm-store 9 | jobs: 10 | version: 11 | timeout-minutes: 15 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: checkout code repository 15 | uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Install Node.js 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: 22 23 | 24 | - uses: pnpm/action-setup@v3 25 | name: Install pnpm 26 | id: pnpm-install 27 | with: 28 | version: 10 29 | run_install: false 30 | 31 | - name: Get pnpm store directory 32 | id: pnpm-cache 33 | shell: bash 34 | run: | 35 | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT 36 | 37 | - uses: actions/cache@v4 38 | name: Setup pnpm cache 39 | with: 40 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} 41 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 42 | restore-keys: | 43 | ${{ runner.os }}-pnpm-store- 44 | 45 | - name: install packages 46 | run: pnpm install --frozen-lockfile 47 | env: 48 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 50 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 51 | 52 | # - name: setup pnpm config 53 | # run: pnpm config set store-dir $PNPM_CACHE_FOLDER 54 | 55 | - name: create and publish versions 56 | uses: changesets/action@v1 57 | with: 58 | version: pnpm ci:version 59 | commit: "chore: update versions" 60 | title: "chore: update versions" 61 | publish: pnpm ci:publish 62 | env: 63 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 64 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | *.log 4 | **/*/.next/ 5 | packages/**/dist/ 6 | !**/.eslintrc*.js 7 | .build/ 8 | dist/ 9 | **/bin/*.js 10 | **/bin/*.js.map 11 | **/bin/*.d.ts 12 | **/bin/*.d.ts.map 13 | tsconfig.tsbuildinfo 14 | .tsbuildinfo 15 | .ultra.cache.json 16 | .jest-cache 17 | .next 18 | .data/ 19 | .npmrc 20 | .cache/ 21 | 22 | .env.local 23 | .env 24 | 25 | # CMake 26 | cmake-build-*/ 27 | 28 | 29 | # File-based project format 30 | *.iws 31 | 32 | # IntelliJ 33 | out/ 34 | 35 | # JIRA plugin 36 | atlassian-ide-plugin.xml 37 | 38 | # Crashlytics plugin (for Android Studio and IntelliJ) 39 | com_crashlytics_export_strings.xml 40 | crashlytics.properties 41 | crashlytics-build.properties 42 | fabric.properties 43 | 44 | # End of https://www.toptal.com/developers/gitignore/api/intellij 45 | 46 | .infra/*_rev.yaml 47 | 48 | # vitest? 49 | *.timestamp-*.mjs 50 | -------------------------------------------------------------------------------- /.ncurc.json: -------------------------------------------------------------------------------- 1 | { 2 | "reject": [ 3 | "glob", 4 | "@types/glob", 5 | "jwks-rsa", 6 | "applicationinsights", 7 | "@faker-js/faker", 8 | "redis", 9 | "@types/redis", 10 | "react-hook-form", 11 | "eslint-plugin-codegen" 12 | ], 13 | "dep": [ 14 | "prod", 15 | "dev", 16 | "optional", 17 | "peer" 18 | ] 19 | } -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # @effect-app:registry=https://npm.pkg.github.com 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v22.11 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "editorconfig.editorconfig", 5 | "Vue.volar" 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "node", 3 | "request": "launch", 4 | "name": "Debug TSC", 5 | "program": "${workspaceFolder}/node_modules/tsc.js", 6 | "args": [ 7 | "--build tsconfig.all.json", 8 | ], 9 | "sourceMaps": false, 10 | "protocol": "inspector", 11 | "configurations": [ 12 | { 13 | "name": "Attach by Process ID", 14 | "processId": "${command:PickProcess}", 15 | "request": "attach", 16 | "skipFiles": [ 17 | "/**" 18 | ], 19 | "type": "node" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /.vscode/model.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "ModelCodegen": { 3 | "prefix": "genmod", 4 | "body": [ 5 | "// codegen:start {preset: model}" 6 | ] 7 | }, 8 | "ResourceCodegen": { 9 | "prefix": "genrsc", 10 | "body": [ 11 | "// codegen:start {preset: meta, sourcePrefix: src/resources/}" 12 | ] 13 | }, 14 | "ModelComplete": { 15 | "prefix": "moc", 16 | "body": [ 17 | "export class $1 extends S.ExtendedClass<$1, $1.Encoded>()({", 18 | "$2", 19 | "}) {}" 20 | ], 21 | "description": "Defines a Model signature" 22 | }, 23 | "Model": { 24 | "prefix": "mo", 25 | "body": [ 26 | "export class $1 extends S.Class<$1>()({$2}) {}", 27 | "" 28 | ], 29 | "description": "Defines a Model signature" 30 | }, 31 | "UnionOpaque": { 32 | "prefix": "un", 33 | "body": [ 34 | "const $1__ = union({ $2 })", 35 | "const $1_ = enhanceClassUnion(OpaqueSchema<$1, $1.Encoded>()($1__))", 36 | "export type $1 = To & UnionBrand", 37 | "export interface $1Schema extends Identity {}", 38 | "export const $1: $1Schema = $1_", 39 | "export namespace $1 {", 40 | " export type Encoded = Encoded & UnionBrand", 41 | "}" 42 | ] 43 | }, 44 | "Request": { 45 | "prefix": "req", 46 | "body": [ 47 | "export class $1 extends S.Req<$1>()(\"$1\", {", 48 | " $2", 49 | "}, { success: $3 }) {}", 50 | "" 51 | ], 52 | "description": "Defines a Request signature" 53 | }, 54 | "Response": { 55 | "prefix": "res", 56 | "body": [ 57 | "export class Response extends S.Class", 68 | " Effect.gen(function*() {", 69 | " //const userRepo = yield* UserRepo", 70 | "", 71 | " return {", 72 | " $2: $2($3)", 73 | " }", 74 | " })" 75 | ] 76 | } 77 | } -------------------------------------------------------------------------------- /.vscode/service.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "ServiceConstructor": { 3 | "prefix": "svc", 4 | "body": [ 5 | "export class $1 extends Effect.Service<$1>()(\"$1\", {", 6 | " dependencies: [$3],", 7 | " effect: Effect.gen(function*() {", 8 | " $2", 9 | " return {}", 10 | " })", 11 | "}) {}", 12 | ] 13 | } 14 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "titleBar.inactiveBackground": "#16a085", 4 | "titleBar.inactiveForeground": "#ffffff" 5 | }, 6 | "explorer.sortOrderLexicographicOptions": "upper", 7 | // would prefer on in frontend, so might want to use a specialised frontend config.. 8 | "volar.updateImportsOnFileMove.enabled": false, 9 | "typescript.preferences.includePackageJsonAutoImports": "on", 10 | "typescript.preferences.autoImportFileExcludePatterns": [w 11 | "node_modules/@sentry/node", 12 | "node_modules/vitest/dist", 13 | "node_modules/@azure/cosmos", 14 | "node_modules/effect-app/Schema", 15 | "node_modules/lodash", 16 | "node_modules/@types/lodash" 17 | ], 18 | "typescript.tsdk": "node_modules/typescript/lib", 19 | "eslint.workingDirectories": [ 20 | { 21 | "pattern": "apps/*" 22 | }, 23 | { 24 | "pattern": "packages/*" 25 | } 26 | ], 27 | "editor.formatOnSave": true, 28 | "editor.codeActionsOnSave": [ 29 | "source.fixAll.eslint", 30 | "source.addMissingImports" 31 | ], 32 | "editor.defaultFormatter": "dbaeumer.vscode-eslint", 33 | "[vue]": { 34 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 35 | }, 36 | "[javascript]": { 37 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 38 | }, 39 | "[javascriptreact]": { 40 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 41 | }, 42 | "[typescript]": { 43 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 44 | }, 45 | "[typescriptreact]": { 46 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 47 | }, 48 | "eslint.validate": [ 49 | "javascript", 50 | "typescript", 51 | "vue" 52 | ], 53 | "eslint.format.enable": true, 54 | "csv-preview.separator": ";", 55 | "cSpell.words": [ 56 | "codegen", 57 | "composables", 58 | "pipeable", 59 | "tsplus" 60 | ], 61 | "files.watcherExclude": { 62 | "**/target": true 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": [ 3 | { 4 | "label": "build", 5 | "type": "shell", 6 | "command": "pnpm watch", 7 | "options": { 8 | "cwd": "${workspaceRoot}" 9 | }, 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | }, 14 | "isBackground": true, 15 | "presentation": { 16 | "group": "watch-build" 17 | }, 18 | "problemMatcher": [ 19 | { 20 | "base": "$tsc-watch", 21 | "fileLocation": [ 22 | "relative", 23 | "${workspaceRoot}", 24 | ], 25 | } 26 | ] 27 | }, 28 | ] 29 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Practical use of Effect-TS 2 | 3 | This is an opinionated library for full-stack [Effect-TS](https://github.com/Effect-TS/core). 4 | (See repositories for more info and discord link, articles, youtube videos, etc). 5 | 6 | WIP [docs](https://github.com/effect-ts-app/docs) 7 | 8 | See https://github.com/effect-ts-app/boilerplate for a sample app use. 9 | 10 | ## Deployment 11 | 12 | Uses [Changesets](https://github.com/changesets/changesets/blob/main/README.md) 13 | 14 | 1. make changes 15 | 2. generate and include changeset `pnpm changeset` 16 | 3. wait for build which creates a PR 17 | 4. inspect the PR, merge when alright 18 | 5. await new build and new package deployments 19 | 20 | ## Thanks 21 | 22 | - All contributors 23 | - Michael Arnaldi, Max Brown and the Effect-TS contributors for Effect 24 | - ZIO Contributors for the excellent ZIO 25 | - Anyone else we're forgetting.. 26 | -------------------------------------------------------------------------------- /eslint.vue.config.mjs: -------------------------------------------------------------------------------- 1 | import formatjs from "eslint-plugin-formatjs" 2 | import pluginVue from "eslint-plugin-vue" 3 | import { defineConfigWithVueTs, vueTsConfigs} from '@vue/eslint-config-typescript'; 4 | import vuePrettierConfig from "@vue/eslint-config-prettier" 5 | 6 | import tseslint from 'typescript-eslint'; 7 | 8 | import { baseConfig } from "./eslint.base.config.mjs" 9 | 10 | /** 11 | * @param {string} dirName 12 | * @param {boolean} [forceTS=false] 13 | * @returns {import("eslint").Linter.FlatConfig[]} 14 | */ 15 | export function vueConfig(dirName, forceTS = false) { 16 | const enableTS = !!dirName && (forceTS || process.env["ESLINT_TS"]) 17 | 18 | return [ 19 | ...baseConfig(dirName, forceTS), 20 | // ...ts.configs.recommended, 21 | // this should set the vue parser as the parser plus some recommended rules 22 | ...pluginVue.configs["flat/recommended"], 23 | ...defineConfigWithVueTs(vueTsConfigs.base), 24 | vuePrettierConfig, 25 | { 26 | name: "vue", 27 | files: ["*.vue", "**/*.vue"], 28 | languageOptions: { 29 | parserOptions: { 30 | // set a custom parser to parse 63 | -------------------------------------------------------------------------------- /packages/vue-components/__tests__/OmegaIntlProvider.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /packages/vue-components/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "types": [ 5 | "vite/client", 6 | "vitest/globals" 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/vue-components/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module "*.vue" { 4 | import { DefineComponent } from "vue" 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const EmailFormComponent: DefineComponent<{}, {}, any> 7 | export default EmailFormComponent 8 | } 9 | -------------------------------------------------------------------------------- /packages/vue-components/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 2 | // @ts-nocheck 3 | 4 | import { vueConfig } from "../../eslint.vue.config.mjs" 5 | 6 | import path from "node:path" 7 | import { fileURLToPath } from "node:url" 8 | 9 | const __filename = fileURLToPath(import.meta.url) 10 | const __dirname = path.dirname(__filename) 11 | 12 | export default [ 13 | ...vueConfig(__dirname, false), 14 | { 15 | ignores: [".output/**", "dist/**", "node_modules/**"], 16 | }, 17 | { 18 | files: ["./**/*.vue"], 19 | rules: { 20 | "vue/multi-word-component-names": "off", 21 | }, 22 | }, 23 | { 24 | rules: { 25 | "@typescript-eslint/no-explicit-any": "warn", 26 | } 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /packages/vue-components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@effect-app/vue-components", 3 | "version": "0.6.3", 4 | "scripts": { 5 | "build": "pnpm build:run", 6 | "build:run": "rimraf dist && vue-tsc && vite build", 7 | "docs:dev": "vitepress dev docs", 8 | "docs:build": "vitepress build docs", 9 | "docs:serve": "vitepress serve docs", 10 | "lint": "NODE_OPTIONS=--max-old-space-size=8192 eslint src", 11 | "autofix": "pnpm lint --fix", 12 | "storybook": "storybook dev -p 6006", 13 | "build-storybook": "storybook build", 14 | "test": "vitest", 15 | "test:run": "vitest run", 16 | "test:watch": "vitest watch" 17 | }, 18 | "peerDependencies": { 19 | "@mdi/js": "^7.4.47", 20 | "@tanstack/vue-form": "^1.2.4", 21 | "effect": "^3.14.2", 22 | "intl-messageformat": "^10.7.16", 23 | "mdi-js": "^1.0.1", 24 | "primeflex": "^4.0.0", 25 | "primeicons": "^7.0.0", 26 | "primevue": "^4.3.3", 27 | "vue": "^3.5.13", 28 | "vuetify": "^3.7.19" 29 | }, 30 | "devDependencies": { 31 | "@storybook/addon-essentials": "^8.6.12", 32 | "@storybook/addon-interactions": "^8.6.12", 33 | "@storybook/blocks": "^8.6.12", 34 | "@storybook/testing-library": "^0.2.2", 35 | "@storybook/vue3": "^8.6.12", 36 | "@storybook/vue3-vite": "^8.6.12", 37 | "@types/node": "^22.13.14", 38 | "@typescript-eslint/eslint-plugin": "8.29.0", 39 | "@typescript-eslint/parser": "8.29.0", 40 | "@vitejs/plugin-vue": "^5.2.3", 41 | "@vue/eslint-config-prettier": "^10.2.0", 42 | "@vue/eslint-config-typescript": "^14.5.0", 43 | "@vue/test-utils": "^2.4.6", 44 | "eslint-plugin-prettier": "^5.2.6", 45 | "eslint-plugin-vue": "^10.0.0", 46 | "jsdom": "^26.1.0", 47 | "rimraf": "^6.0.1", 48 | "sass": "^1.86.0", 49 | "storybook": "^8.6.12", 50 | "typescript": "^5.8.2", 51 | "vite": "^6.2.3", 52 | "vite-plugin-css-injected-by-js": "^3.5.2", 53 | "vitepress": "^1.6.3", 54 | "vue-tsc": "^2.2.8" 55 | }, 56 | "files": [ 57 | "src", 58 | "dist" 59 | ], 60 | "module": "./dist/vue-components.es.js", 61 | "exports": { 62 | ".": { 63 | "types": "./dist/types/index.d.ts", 64 | "import": "./dist/vue-components.es.js" 65 | }, 66 | "./dist/vue-components.css": "./dist/vue-components.css" 67 | }, 68 | "dependencies": { 69 | "@effect-app/vue": "workspace:*", 70 | "effect-app": "workspace:*", 71 | "highlight.js": "^11.11.1", 72 | "vue3-highlightjs": "^1.0.5" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /packages/vue-components/src/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/effect-app/libs/0f1458333dd30378d9d7b6f365f0eed2e1d4da44/packages/vue-components/src/assets/.keep -------------------------------------------------------------------------------- /packages/vue-components/src/components/OmegaForm/OmegaErrorsContext.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type InjectionKey, 3 | provide, 4 | inject, 5 | ref, 6 | readonly, 7 | type Ref, 8 | computed, 9 | } from "vue" 10 | import { type OmegaError, type ShowErrorsOn } from "./OmegaFormStuff" 11 | import type { StandardSchemaV1Issue } from "@tanstack/vue-form" 12 | 13 | export const OmegaErrorsKey = Symbol() as InjectionKey<{ 14 | errors: Ref 15 | addError: (error: OmegaError) => void 16 | removeError: (inputId: string) => void 17 | clearErrors: () => void 18 | showErrors: Ref 19 | showErrorsOn: ShowErrorsOn 20 | generalErrors: Ref< 21 | (Record | undefined)[] | undefined 22 | > 23 | }> 24 | 25 | export function provideOmegaErrors( 26 | formSubmissionAttempts: Ref, 27 | generalErrors: Ref< 28 | (Record | undefined)[] | undefined 29 | >, 30 | showErrorsOn: ShowErrorsOn = "onSubmit", 31 | ) { 32 | const errors = ref([]) 33 | 34 | const removeError = (inputId: string) => { 35 | errors.value = errors.value.filter(error => error.inputId !== inputId) 36 | } 37 | 38 | const addError = (error: OmegaError) => { 39 | removeError(error.inputId) 40 | errors.value.push(error) 41 | } 42 | 43 | const clearErrors = () => { 44 | errors.value = [] 45 | } 46 | 47 | const showErrors = computed(() => { 48 | switch (showErrorsOn) { 49 | case "onChange": 50 | return true 51 | default: 52 | return formSubmissionAttempts.value > 0 53 | } 54 | }) 55 | 56 | const context = { 57 | errors: readonly(errors), 58 | addError, 59 | removeError, 60 | clearErrors, 61 | showErrors, 62 | generalErrors, 63 | showErrorsOn: showErrorsOn ?? "onSubmit", 64 | } 65 | 66 | provide(OmegaErrorsKey, context) 67 | 68 | return context 69 | } 70 | 71 | export function useOmegaErrors() { 72 | const context = inject(OmegaErrorsKey) 73 | if (!context) { 74 | throw new Error("useOmegaErrors must be used within an OmegaForm provider") 75 | } 76 | return context 77 | } 78 | -------------------------------------------------------------------------------- /packages/vue-components/src/components/OmegaForm/OmegaFormInput.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 46 | -------------------------------------------------------------------------------- /packages/vue-components/src/components/OmegaForm/OmegaInput.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 53 | -------------------------------------------------------------------------------- /packages/vue-components/src/components/OmegaForm/getOmegaStore.ts: -------------------------------------------------------------------------------- 1 | import { useStore } from "@tanstack/vue-form" 2 | import { type Ref, computed } from "vue" 3 | import type { OmegaFormState, OmegaFormApi } from "./OmegaFormStuff" 4 | 5 | export function getOmegaStore< 6 | To, 7 | From, 8 | K extends keyof OmegaFormState = keyof OmegaFormState, 9 | >( 10 | form: OmegaFormApi, 11 | subscribe?: K[], 12 | ): Ref< 13 | K[] extends undefined[] 14 | ? Record 15 | : Pick, K> 16 | > { 17 | return computed(() => { 18 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 19 | if (!subscribe) return {} as any 20 | 21 | const state = useStore(form.store, state => { 22 | const result = {} as Pick, K> 23 | for (const key of subscribe) { 24 | result[key] = state[key] 25 | } 26 | return result 27 | }) 28 | 29 | return state.value 30 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 31 | }) as any // Type assertion needed due to Vue's computed typing limitations 32 | } 33 | -------------------------------------------------------------------------------- /packages/vue-components/src/components/OmegaForm/index.ts: -------------------------------------------------------------------------------- 1 | import { default as OmegaForm } from "./OmegaWrapper.vue" 2 | import { default as OmegaInput } from "./OmegaInput.vue" 3 | import { default as OmegaErrors } from "./OmegaErrors.vue" 4 | import { default as OmegaAutoGen } from "./OmegaAutoGen.vue" 5 | 6 | export * as OmegaErrorsContext from "./OmegaErrorsContext" 7 | export * from "./OmegaFormStuff" 8 | export { useOmegaForm, type OmegaFormReturn } from "./useOmegaForm" 9 | export { default } from "./OmegaWrapper.vue" 10 | 11 | export { OmegaForm, OmegaInput, OmegaErrors, OmegaAutoGen } 12 | -------------------------------------------------------------------------------- /packages/vue-components/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./OmegaForm" 2 | -------------------------------------------------------------------------------- /packages/vue-components/src/components/style.css: -------------------------------------------------------------------------------- 1 | .cool { 2 | color: pink; 3 | } 4 | -------------------------------------------------------------------------------- /packages/vue-components/src/constants/index.ts: -------------------------------------------------------------------------------- 1 | export {} 2 | -------------------------------------------------------------------------------- /packages/vue-components/src/index.ts: -------------------------------------------------------------------------------- 1 | import { type App } from "vue" 2 | import * as components from "./components" 3 | 4 | function install(app: App) { 5 | for (const key in components) { 6 | if (Object.prototype.hasOwnProperty.call(components, key)) { 7 | const component = components[key as keyof typeof components] 8 | if (component && typeof component === "object") { 9 | app.component(key, component) 10 | } 11 | } 12 | } 13 | } 14 | 15 | // import './assets/main.scss' 16 | 17 | export default { install } 18 | 19 | export * from "./components" 20 | export * from "./constants" 21 | export * from "./utils" 22 | -------------------------------------------------------------------------------- /packages/vue-components/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { inject, type InjectionKey, provide } from "vue" 2 | import { type makeIntl } from "@effect-app/vue" 3 | 4 | const intlKey = Symbol() as InjectionKey< 5 | ReturnType["useIntl"]> 6 | > 7 | export const useIntl = () => { 8 | const intl = inject(intlKey) 9 | if (!intl) { 10 | throw new Error("useIntl must be used within a IntlProvider") 11 | } 12 | return intl 13 | } 14 | export const provideIntl = ( 15 | intl: ReturnType["useIntl"]>, 16 | ) => provide(intlKey, intl) 17 | -------------------------------------------------------------------------------- /packages/vue-components/stories/OmegaForm/AutoGeneration.vue: -------------------------------------------------------------------------------- 1 | 65 | 66 | 86 | -------------------------------------------------------------------------------- /packages/vue-components/stories/OmegaForm/Booleans.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /packages/vue-components/stories/OmegaForm/Clearable.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 21 | -------------------------------------------------------------------------------- /packages/vue-components/stories/OmegaForm/EmailForm.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 53 | -------------------------------------------------------------------------------- /packages/vue-components/stories/OmegaForm/Meta.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 58 | -------------------------------------------------------------------------------- /packages/vue-components/stories/OmegaForm/PersistencyForm.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 63 | -------------------------------------------------------------------------------- /packages/vue-components/stories/OmegaForm/SimpleForm.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 31 | -------------------------------------------------------------------------------- /packages/vue-components/stories/OmegaForm/SimpleFormVuetifyDefault.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | -------------------------------------------------------------------------------- /packages/vue-components/stories/OmegaForm/SumExample.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 42 | -------------------------------------------------------------------------------- /packages/vue-components/stories/README.md: -------------------------------------------------------------------------------- 1 | # Storybook Stories 2 | 3 | This folder contains all the Storybook stories for the Vue Components package. Each story file corresponds to a component and demonstrates its various use cases and configurations. 4 | 5 | ## Structure 6 | 7 | - Each story file is named after the component it's documenting (e.g., `OmegaForm.stories.ts`) 8 | - Stories are organized by component type and functionality 9 | - Each story includes proper TypeScript typing and documentation 10 | 11 | ## Running Stories 12 | 13 | To run the Storybook: 14 | 15 | ```bash 16 | pnpm storybook 17 | ``` 18 | 19 | This will start Storybook on port 6006, and you can view your components at http://localhost:6006. 20 | 21 | ## Adding New Stories 22 | 23 | When adding a new story: 24 | 25 | 1. Create a new file in this folder with the naming pattern `ComponentName.stories.ts` 26 | 2. Import the component and any dependencies 27 | 3. Define the meta object with component information 28 | 4. Create story objects that demonstrate different use cases 29 | 5. Use proper TypeScript typing for all parameters 30 | -------------------------------------------------------------------------------- /packages/vue-components/stories/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "exclude": [] 4 | } 5 | -------------------------------------------------------------------------------- /packages/vue-components/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "useDefineForClassFields": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "esModuleInterop": true, 12 | "lib": ["esnext", "dom"], 13 | "declaration": true, 14 | "emitDeclarationOnly": true, 15 | "declarationDir": "./dist/types", 16 | "isolatedModules": true, 17 | "skipLibCheck": true, 18 | "types": [ 19 | "vite/client" 20 | ] 21 | }, 22 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "src/index.js"], 23 | "exclude": ["src/stories/**", "**/__tests__/**", "**/*.test.*"] 24 | } 25 | -------------------------------------------------------------------------------- /packages/vue-components/vitest.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { defineConfig } from "vite" 3 | import vue from '@vitejs/plugin-vue' 4 | import makeConfig from "../../vite.config.base" 5 | 6 | export default defineConfig({ 7 | ...makeConfig(__dirname), 8 | plugins: [vue()], 9 | test: { 10 | environment: 'jsdom', 11 | include: ['src/**/*.test.{js,ts,jsx,tsx}', '**/__tests__/**/*.test.{js,ts,jsx,tsx}'], 12 | exclude: ['node_modules/**', 'dist/**'], 13 | globals: true 14 | }, 15 | optimizeDeps: { 16 | exclude: ['**/__tests__/**', '**/*.test.*'] 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /packages/vue/.madgerc: -------------------------------------------------------------------------------- 1 | { 2 | "detectiveOptions": { 3 | "ts": { 4 | "skipTypeImports": true 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/vue/.npmignore: -------------------------------------------------------------------------------- 1 | *.test.js 2 | *.test.d.ts 3 | tsconfig.build.json 4 | *.log 5 | tslint.json 6 | .eslintrc.js 7 | .eslintignore 8 | .madgerc 9 | .swcrc 10 | .cache 11 | coverage/ 12 | .tsbuildinfo 13 | .build/ 14 | !dist/ 15 | -------------------------------------------------------------------------------- /packages/vue/.prettierignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | 3 | **/dist/ 4 | **/*.js 5 | **/*.jsx 6 | **/*.d.ts 7 | -------------------------------------------------------------------------------- /packages/vue/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import path from "node:path" 2 | import { fileURLToPath } from "node:url" 3 | import { augmentedConfig } from "../../eslint.base.config.mjs" 4 | 5 | const __filename = fileURLToPath(import.meta.url) 6 | const __dirname = path.dirname(__filename) 7 | 8 | export default [ 9 | ...augmentedConfig(__dirname, false), 10 | { 11 | ignores: [ 12 | "**/*.js", 13 | "**/*.jsx", 14 | "**/*.d.ts", 15 | "node_modules/" 16 | ] 17 | }, 18 | { 19 | rules: { 20 | "@typescript-eslint/no-empty-interface": "off", 21 | "@typescript-eslint/no-explicit-any": "warn", 22 | } 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /packages/vue/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./lib.js" 2 | 3 | export * from "./makeClient.js" 4 | export * from "./makeContext.js" 5 | export * from "./makeIntl.js" 6 | export * from "./mutate.js" 7 | export * from "./query.js" 8 | export * from "./runtime.js" 9 | -------------------------------------------------------------------------------- /packages/vue/src/makeContext.ts: -------------------------------------------------------------------------------- 1 | import { inject, type InjectionKey, provide } from "vue" 2 | 3 | export const makeContext = (def: T) => { 4 | const key = Symbol() as InjectionKey 5 | return { 6 | use: () => inject(key, def), 7 | provide: (locale: T) => provide(key, locale) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/vue/src/makeIntl.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-empty-object-type */ 2 | import { createIntl, createIntlCache, type Formatters, type IntlFormatters, type ResolvedIntlConfig } from "@formatjs/intl" 3 | import { typedKeysOf } from "effect-app/utils" 4 | import type { FormatXMLElementFn, PrimitiveType } from "intl-messageformat" 5 | import { computed, type Ref, ref, watch } from "vue" 6 | import { translate } from "./form.js" 7 | import { makeContext } from "./makeContext.js" 8 | 9 | export interface MakeIntlReturn extends ReturnType> {} 10 | 11 | // Re-export in the hope to deal with ts issues 12 | export interface IntlShape extends ResolvedIntlConfig, IntlFormatters { 13 | formatters: Formatters 14 | } 15 | 16 | export const makeIntl = ( 17 | messages: Record>, 18 | defaultLocale: NoInfer 19 | ) => { 20 | const intlCache = createIntlCache() 21 | 22 | const intls = typedKeysOf(messages).reduce( 23 | (acc, cur) => { 24 | acc[cur] = createIntl( 25 | { 26 | defaultLocale, 27 | locale: cur, 28 | messages: messages[cur] 29 | }, 30 | intlCache 31 | ) 32 | return acc 33 | }, 34 | {} as Record> 35 | ) 36 | 37 | const LocaleContext = makeContext(ref(defaultLocale) as Ref) 38 | 39 | const useIntl = () => { 40 | const locale = LocaleContext.use() 41 | 42 | const trans = ( 43 | id: keyof (typeof messages)[Locale], 44 | values?: Record< 45 | string, 46 | PrimitiveType | FormatXMLElementFn 47 | > 48 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any 49 | ) => intls[locale.value].formatMessage({ id: id as any }, values) 50 | 51 | const intl = computed(() => intls[locale.value]) 52 | watch( 53 | locale, 54 | (locale) => { 55 | const intl = intls[locale] 56 | translate.value = intl.formatMessage 57 | }, 58 | { immediate: true } 59 | ) 60 | 61 | return { locale, trans, intl } 62 | } 63 | return { useIntl, LocaleContext } 64 | } 65 | -------------------------------------------------------------------------------- /packages/vue/src/routeParams.ts: -------------------------------------------------------------------------------- 1 | import { flow } from "effect" 2 | import { Option, S } from "effect-app" 3 | import type { Schema } from "effect-app/Schema" 4 | import { typedKeysOf } from "effect-app/utils" 5 | import type { ParsedQuery } from "query-string" 6 | 7 | export function getQueryParam(search: ParsedQuery, param: string) { 8 | const v = search[param] 9 | if (Array.isArray(v)) { 10 | return v[0] 11 | } 12 | return v ?? null 13 | } 14 | 15 | export const getQueryParamO = flow(getQueryParam, Option.fromNullable) 16 | 17 | export const parseOpt = (t: S.Schema) => { 18 | const dec = flow(S.decodeUnknownEither(t), (x) => 19 | x._tag === "Right" 20 | ? Option.some(x.right) 21 | : Option.none()) 22 | return dec 23 | } 24 | 25 | export const parseOptUnknown = (t: S.Schema) => { 26 | const dec = flow(S.decodeUnknownEither(t), (x) => 27 | x._tag === "Right" 28 | ? Option.some(x.right) 29 | : Option.none()) 30 | return dec 31 | } 32 | 33 | export function parseRouteParamsOption>>( 34 | query: Record, 35 | t: NER // enforce non empty 36 | ): { 37 | [K in keyof NER]: Option> 38 | } { 39 | return typedKeysOf(t).reduce( 40 | (prev, cur) => { 41 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 42 | prev[cur] = getQueryParamO(query, cur as string).pipe(Option.flatMap(parseOpt(t[cur]!))) 43 | 44 | return prev 45 | }, 46 | {} as { 47 | [K in keyof NER]: Option> 48 | } 49 | ) 50 | } 51 | 52 | export function parseRouteParams>>( 53 | query: Record, 54 | t: NER // enforce non empty 55 | ): { 56 | [K in keyof NER]: Schema.Type 57 | } { 58 | return typedKeysOf(t).reduce( 59 | (prev, cur) => { 60 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 61 | prev[cur] = S.decodeUnknownSync(t[cur]!)((query as any)[cur]) 62 | 63 | return prev 64 | }, 65 | {} as { 66 | [K in keyof NER]: Schema.Type 67 | } 68 | ) 69 | } 70 | -------------------------------------------------------------------------------- /packages/vue/src/runtime.ts: -------------------------------------------------------------------------------- 1 | import { Exit, Runtime } from "effect" 2 | import { Effect, Layer, Logger } from "effect-app" 3 | import * as Scope from "effect/Scope" 4 | 5 | export function makeAppRuntime(layer: Layer) { 6 | return Effect.gen(function*() { 7 | layer = layer.pipe( 8 | Layer.provide(Logger.replace(Logger.defaultLogger, Logger.withSpanAnnotations(Logger.prettyLogger()))) 9 | ) 10 | const scope = yield* Scope.make() 11 | const env = yield* layer.pipe(Layer.buildWithScope(scope)) 12 | const runtime = yield* Effect.runtime().pipe(Effect.scoped, Effect.provide(env)) 13 | 14 | return { 15 | runtime: Object.assign(runtime, { 16 | runPromise: Runtime.runPromise(runtime), 17 | runPromiseExit: Runtime.runPromiseExit(runtime), 18 | runSync: Runtime.runSync(runtime), 19 | runSyncExit: Runtime.runSyncExit(runtime), 20 | runFork: Runtime.runFork(runtime) 21 | }), 22 | clean: Scope.close(scope, Exit.void) 23 | } 24 | }) 25 | } 26 | 27 | export function initializeSync(layer: Layer) { 28 | const { clean, runtime } = Effect.runSync(makeAppRuntime(layer)) 29 | return { 30 | runtime, 31 | clean: () => Effect.runSync(clean) 32 | } 33 | } 34 | 35 | export function initializeAsync(layer: Layer) { 36 | return Effect 37 | .runPromise(makeAppRuntime(layer)) 38 | .then(({ clean, runtime }) => { 39 | return { 40 | runtime, 41 | clean: () => Effect.runPromise(clean) 42 | } 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /packages/vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": {}, 3 | "extends": "../../tsconfig.base.json", 4 | "references": [ 5 | { 6 | "path": "./tsconfig.src.json" 7 | }, 8 | { 9 | "path": "./tsconfig.test.json" 10 | } 11 | ], 12 | } -------------------------------------------------------------------------------- /packages/vue/tsconfig.src.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "lib": [ 5 | "esnext" 6 | ], 7 | "tsBuildInfoFile": "./dist/.tsbuildinfo", 8 | "esModuleInterop": true, 9 | "rootDir": "./src", 10 | // keep in here, cause madge can't detect it from extended tsconfig 11 | "moduleResolution": "Node16", 12 | "outDir": "./dist" 13 | }, 14 | "include": [ 15 | "./src/**/*.ts" 16 | ], 17 | "exclude": [ 18 | "./dist", 19 | "*.test.ts", 20 | "node_modules", 21 | "build", 22 | "lib", 23 | "dist", 24 | "**/*.d.ts.map", 25 | ".git", 26 | ".data", 27 | "**/.*", 28 | "**/*.tmp" 29 | ], 30 | "references": [ 31 | { 32 | "path": "../effect-app" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /packages/vue/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.src.json", 3 | "compilerOptions": { 4 | "rootDir": "./test", 5 | "outDir": "./test/dist", 6 | "tsBuildInfoFile": "./test/dist/.tsbuildinfo", 7 | // "types": [ 8 | // "vitest/globals" 9 | // ] 10 | }, 11 | "include": [ 12 | "./test/**/*.ts" 13 | ], 14 | "exclude": [ 15 | "./dist", 16 | "./test/dist", 17 | "node_modules", 18 | "build", 19 | "lib", 20 | "dist", 21 | "**/*.d.ts.map", 22 | ".git", 23 | ".data", 24 | "**/.*", 25 | "**/*.tmp" 26 | ], 27 | "references": [ 28 | { 29 | "path": "./tsconfig.src.json" 30 | }, 31 | ] 32 | } -------------------------------------------------------------------------------- /packages/vue/vitest.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { defineConfig } from "vite" 3 | import makeConfig from "../../vite.config.base" 4 | 5 | export default defineConfig(makeConfig(__dirname)) 6 | -------------------------------------------------------------------------------- /packages/vue/wallaby.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require("../../wallaby.base.cjs") 2 | -------------------------------------------------------------------------------- /patches/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/effect-app/libs/0f1458333dd30378d9d7b6f365f0eed2e1d4da44/patches/.gitkeep -------------------------------------------------------------------------------- /patches/eslint-plugin-codegen@0.17.0.patch: -------------------------------------------------------------------------------- 1 | diff --git a/dist/index.js b/dist/index.js 2 | index 69126edc70f6abdf3ba0306db22059fc812bfa89..6ea6404fedc663e7b74357503e38c02d141c8ee7 100644 3 | --- a/dist/index.js 4 | +++ b/dist/index.js 5 | @@ -55,7 +55,7 @@ exports.processors = { 6 | }; 7 | const codegen = { 8 | // @ts-expect-error types are wrong? 9 | - meta: { fixable: true }, 10 | + meta: { fixable: true, schema: false }, 11 | create(context) { 12 | const validate = () => { 13 | const sourceCode = context 14 | @@ -115,7 +115,7 @@ const codegen = { 15 | return; 16 | } 17 | const opts = maybeOptions.right || {}; 18 | - const presets = Object.assign(Object.assign({}, presetsModule), (_a = context.options[0]) === null || _a === void 0 ? void 0 : _a.presets); 19 | + const presets = Object.assign(Object.assign({}, presetsModule), (_a = context.options[0]) === null || _a === void 0 ? void 0 : typeof _a.presets === "string" ? require(_a.presets) : _a.presets); 20 | const preset = typeof (opts === null || opts === void 0 ? void 0 : opts.preset) === 'string' && presets[opts.preset]; 21 | if (typeof preset !== 'function') { 22 | context.report({ 23 | @@ -129,7 +129,7 @@ const codegen = { 24 | const normalise = (val) => val.trim().replace(/\r?\n/g, os.EOL); 25 | const result = (0, Either_1.tryCatch)(() => { 26 | const meta = { filename: context.getFilename(), existingContent }; 27 | - return preset({ meta, options: opts }); 28 | + return preset({ meta, options: opts }, context); 29 | }, err => `${err}`); 30 | if (result._tag === 'Left') { 31 | context.report({ message: result.left, loc: startMarkerLoc }); 32 | diff --git a/dist/presets/index.d.ts b/dist/presets/index.d.ts 33 | index a919405f6aeb9f3d3887d69d10350b46b790cf8e..3122de22e92cdf2d95d8bd881d054b35eb223f0c 100644 34 | --- a/dist/presets/index.d.ts 35 | +++ b/dist/presets/index.d.ts 36 | @@ -4,7 +4,7 @@ export declare type Preset = (params: { 37 | existingContent: string; 38 | }; 39 | options: Options; 40 | -}) => string; 41 | +}, context: any) => string; 42 | export * from './barrel'; 43 | export * from './custom'; 44 | export * from './empty'; 45 | -------------------------------------------------------------------------------- /patches/ts-plugin-sort-import-suggestions.patch: -------------------------------------------------------------------------------- 1 | diff --git a/index.js b/index.js 2 | index 2ae5c7f6e1066e198c73c72791569e62953fcb84..58104dc542df9b29c2a5bd3811e0498b6705067e 100644 3 | --- a/index.js 4 | +++ b/index.js 5 | @@ -44,22 +44,24 @@ function init(modules) { 6 | }); 7 | return prior; 8 | }; 9 | - proxy.getCodeFixesAtPosition = (fileName, start, end, errorCodes, formatOptions, preferences) => { 10 | - const prior = info.languageService.getCodeFixesAtPosition( 11 | - fileName, 12 | - start, 13 | - end, 14 | - errorCodes, 15 | - formatOptions, 16 | - preferences 17 | - ); 18 | - const newFixes = [...prior].sort((a, b) => { 19 | - const aSort = moveUpRegexes.some((re) => re.test(a.description)) ? -1 : moveDownRegexes.some((re) => re.test(a.description)) ? 1 : 0; 20 | - const bSort = moveUpRegexes.some((re) => re.test(b.description)) ? -1 : moveDownRegexes.some((re) => re.test(b.description)) ? 1 : 0; 21 | - return aSort - bSort; 22 | - }); 23 | - return newFixes; 24 | - }; 25 | + // We patched TS itself to do the following, so we had more control over what is going on 26 | + // 27 | + // proxy.getCodeFixesAtPosition = (fileName, start, end, errorCodes, formatOptions, preferences) => { 28 | + // const prior = info.languageService.getCodeFixesAtPosition( 29 | + // fileName, 30 | + // start, 31 | + // end, 32 | + // errorCodes, 33 | + // formatOptions, 34 | + // preferences 35 | + // ); 36 | + // const newFixes = [...prior].sort((a, b) => { 37 | + // const aSort = moveUpRegexes.some((re) => re.test(a.description)) ? -1 : moveDownRegexes.some((re) => re.test(a.description)) ? 1 : 0; 38 | + // const bSort = moveUpRegexes.some((re) => re.test(b.description)) ? -1 : moveDownRegexes.some((re) => re.test(b.description)) ? 1 : 0; 39 | + // return aSort - bSort; 40 | + // }); 41 | + // return newFixes; 42 | + // }; 43 | return proxy; 44 | } 45 | return { create }; 46 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/* 3 | -------------------------------------------------------------------------------- /scripts/clean-dist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for d in `find dist -type d | grep -v dist$` 4 | do 5 | src_d="src/${d#dist/}" 6 | if [ ! -d "$src_d" ]; then 7 | echo "Removing $d" 8 | rm -rf $d 9 | fi 10 | done 11 | 12 | for f in `find dist -type f | grep \\\.mjs$` 13 | do 14 | src_f="src/${f#dist/}" 15 | src_f="${src_f%.mjs}.mts" 16 | src_f2="${src_f}x" 17 | raw="${f%.mjs}" 18 | if [ ! -f "$src_f" ]; then 19 | if [ ! -f "$src_f2" ]; then 20 | echo "Removing $raw.mjs" 21 | rm -f $raw.mjs $raw.mjs.map $raw.d.mts $raw.d.mts.map 22 | fi 23 | fi 24 | done 25 | 26 | 27 | for f in `find dist -type f | grep \\\.js$` 28 | do 29 | src_f="src/${f#dist/}" 30 | src_f="${src_f%.js}.ts" 31 | src_f2="${src_f}x" 32 | raw="${f%.js}" 33 | if [ ! -f "$src_f" ]; then 34 | if [ ! -f "$src_f2" ]; then 35 | if [ "$raw" != "dist/internal/Prelude" ]; then 36 | echo "Removing $raw.js" 37 | rm -f $raw.js $raw.js.map $raw.d.ts $raw.d.ts.map 38 | fi 39 | fi 40 | fi 41 | done -------------------------------------------------------------------------------- /scripts/convert_patch.js: -------------------------------------------------------------------------------- 1 | import fs from "fs" 2 | 3 | fs.readdirSync("./patches").filter(_ => _.slice(1).includes("@")).forEach((f) => 4 | { 5 | let [packageName_, maybeVersion, version] = f.replace(".patch", "").split("@") 6 | const packageName = (version ? ("@" + maybeVersion) : packageName_).split("__").join("/") 7 | version = version ?? maybeVersion 8 | const oldPatchFile = `./patches/${f}` 9 | const content = fs.readFileSync(oldPatchFile, "utf-8").replaceAll(" a/", ` a/node_modules/${packageName}/`).replaceAll(" b/", ` b/node_modules/${packageName}/`) 10 | const newPatchFile = `./patches/${packageName.replace("/", "+")}+${version}.patch` 11 | fs.writeFileSync(newPatchFile, content) 12 | 13 | fs.rmSync(oldPatchFile) 14 | }) -------------------------------------------------------------------------------- /scripts/extract.sh: -------------------------------------------------------------------------------- 1 | for f in `find src -type f | grep .ts$ | grep -v \\\.test.ts` 2 | do 3 | f1=`echo $f | cut -c 5-` 4 | f=./$f1 5 | f2="./dist${f#.}" 6 | f2="${f2%.ts}.js" 7 | 8 | 9 | echo "\"${f%.ts}\": { \"types\": \"${f2%.js}.d.ts\", \"default\": \"$f2\" }," 10 | done 11 | -------------------------------------------------------------------------------- /scripts/mergeTsConfig.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _fs = _interopRequireDefault(require("fs")); 4 | var _json = _interopRequireDefault(require("json5")); 5 | var _path = _interopRequireDefault(require("path")); 6 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 7 | //import ts from "typescript" 8 | const configPath = process.argv[2]; 9 | console.log(configPath); 10 | const rootConfig = _json.default.parse(_fs.default.readFileSync(configPath, "utf-8").toString()); 11 | const configs = [rootConfig]; 12 | let currentConfig = rootConfig; 13 | let relativeRoot = _path.default.dirname(_path.default.resolve(configPath)); 14 | while (currentConfig) { 15 | if (currentConfig.extends) { 16 | let extendsPath = _path.default.resolve(relativeRoot, currentConfig.extends); 17 | if (!_fs.default.existsSync(extendsPath)) { 18 | extendsPath = _path.default.resolve(relativeRoot, "node_modules", currentConfig.extends); 19 | } 20 | const c = _json.default.parse(_fs.default.readFileSync(extendsPath, "utf-8").toString()); 21 | configs.push(c); 22 | currentConfig = c; 23 | relativeRoot = _path.default.dirname(_path.default.resolve(extendsPath)); 24 | } else { 25 | currentConfig = undefined; 26 | } 27 | } 28 | const config = [...configs].reverse().reduce((prev, cur) => { 29 | const { 30 | compilerOptions, 31 | ...rest 32 | } = cur; 33 | Object.assign(prev, rest); 34 | Object.assign(prev.compilerOptions, compilerOptions); 35 | return prev; 36 | }, { 37 | compilerOptions: {} 38 | }); 39 | if (config.compilerOptions.tsPlusConfig) { 40 | _fs.default.cpSync(config.compilerOptions.tsPlusConfig, "./tsplus.config.json"); 41 | Object.assign(config.compilerOptions, { 42 | tsPlusConfig: "./tsplus.config.json", 43 | noEmit: true 44 | }); 45 | } 46 | Object.assign(config, { 47 | extends: undefined, 48 | references: [] 49 | }); 50 | console.log(config); 51 | _fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2)); 52 | //# sourceMappingURL=mergeTsConfig.cjs.map -------------------------------------------------------------------------------- /scripts/sort.js: -------------------------------------------------------------------------------- 1 | const { readFileSync, writeFileSync } = require("fs") 2 | 3 | const file = process.argv[2] 4 | const root = JSON.parse(readFileSync(file, "utf-8")) 5 | 6 | const sorted = Object.keys(root) 7 | .sort((a, b) => a.localeCompare(b)) 8 | .reduce((acc, key) => ({ 9 | ...acc, 10 | [key]: root[key].sort((a, b) => { 11 | const aIsType = a.definitionKind === "type" || a.definitionKind === "interface"; 12 | const bIsType = b.definitionKind === "type" || b.definitionKind === "interface"; 13 | 14 | if(aIsType !== bIsType) { 15 | if(aIsType) return -1; 16 | return 1; 17 | } 18 | 19 | 20 | const aIsUpperCase = a.definitionName[0].toUpperCase() === a.definitionName[0]; 21 | const bIsUpperCase = b.definitionName[0].toUpperCase() === b.definitionName[0]; 22 | 23 | if(aIsUpperCase !== bIsUpperCase) { 24 | if(aIsUpperCase) return -1; 25 | return 1; 26 | } 27 | return a.definitionName.localeCompare(b.definitionName); 28 | }) 29 | }), {}) 30 | 31 | writeFileSync(file, JSON.stringify(sorted, undefined, 2), "utf-8") 32 | -------------------------------------------------------------------------------- /tsconfig.all.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "include": [], 4 | "exclude": [ 5 | "**/.jest-cache", 6 | "**/node_modules", 7 | "**/build", 8 | "**/lib", 9 | "**/dist", 10 | "**/.*" 11 | ], 12 | "references": [ 13 | { 14 | "path": "packages/cli" 15 | }, 16 | { 17 | "path": "packages/eslint-codegen-model" 18 | }, 19 | { 20 | "path": "packages/effect-app" 21 | }, 22 | { 23 | "path": "packages/infra" 24 | }, 25 | { 26 | "path": "packages/vue" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/strictest/tsconfig.json", 3 | "compilerOptions": { 4 | "module": "Node16", 5 | "lib": [ 6 | "ES2023" 7 | ], 8 | "target": "es2022", 9 | // inline source maps stay better in sync with the source, which should aid with watchers. 10 | //"sourceMap": true, 11 | "inlineSourceMap": true, 12 | "incremental": true, 13 | "composite": true, 14 | "declarationMap": true, 15 | "experimentalDecorators": true, 16 | "emitDecoratorMetadata": true, 17 | "noImplicitAny": true, 18 | "useUnknownInCatchVariables": true, 19 | // This is not desirable, as it's useful in generators. 20 | "noImplicitReturns": false, 21 | "noImplicitThis": true, 22 | "outDir": "build/dist", 23 | "resolveJsonModule": true, 24 | "moduleResolution": "Node16", 25 | "downlevelIteration": true, 26 | "noErrorTruncation": true, 27 | "forceConsistentCasingInFileNames": true, 28 | //"preserveSymlinks": true 29 | "types": [ 30 | "vitest/globals" 31 | ], 32 | }, 33 | "watchOptions": { 34 | // Use native file system events for files and directories 35 | // This actually detect changes in parent directories ... not great! 36 | // however it seems that the response is very fast on the non-related projects figuring out their files are unchanged... 37 | "watchFile": "useFsEvents", 38 | "watchDirectory": "useFsEvents", 39 | // // Poll files for updates more frequently 40 | // // when they're updated a lot. 41 | "fallbackPolling": "dynamicPriority", 42 | "excludeDirectories": [ 43 | "**/node_modules", 44 | "**/dist", 45 | "**/.build", 46 | "**/.git", 47 | "**/.data", 48 | "**/.logs", 49 | "**/.*", 50 | ], 51 | "excludeFiles": [ 52 | "**/*.tmp", 53 | "openapi.json", 54 | "*.json" 55 | ] 56 | }, 57 | "include": [], 58 | "exclude": [ 59 | "**/node_modules", 60 | "**/build", 61 | "**/dist", 62 | "**/.*" 63 | ] 64 | } -------------------------------------------------------------------------------- /tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | // "moduleResolution": "Node16", 5 | // "outDir": "./dist", 6 | // "baseUrl": "./", 7 | // "rootDir": "./src", 8 | // "noEmit": false, 9 | // temp 10 | "noImplicitAny": false, 11 | "noPropertyAccessFromIndexSignature": false, 12 | "noUncheckedIndexedAccess": false, 13 | } 14 | } -------------------------------------------------------------------------------- /tsconfig.plugins.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "plugins": [ 4 | { 5 | "name": "ts-plugin-sort-import-suggestions", 6 | "moveUpPatterns": [ 7 | "\\.{1,2}/", 8 | "^(?:\\.\\./)+", 9 | "^#", 10 | "^@/", 11 | "effect", 12 | "^@effect/" 13 | ], 14 | "moveDownPatterns": [ 15 | "^node_modules/" 16 | ], 17 | "overrides": { 18 | "effect-app": [] 19 | } 20 | }, 21 | { 22 | "name": "@effect/language-service" 23 | } 24 | ] 25 | } 26 | } -------------------------------------------------------------------------------- /vite.config.base.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import path from "path" 3 | import fs from "fs" 4 | import AutoImport from "unplugin-auto-import/vite" 5 | import { defineConfig } from "vitest/config" 6 | 7 | // const autoImport = AutoImport({ 8 | // dts: "./test/auto-imports.d.ts", 9 | // // include: [ 10 | // // /\.test\.[tj]sx?$/ // .ts, .tsx, .js, .jsx 11 | // // ], 12 | // imports: [ 13 | // "vitest" 14 | // ] 15 | // }) 16 | 17 | export default function makeConfig(dirName?: string) { 18 | const prefix = path.resolve(__dirname, "packages") 19 | const packages = fs.readdirSync(prefix).map(f => prefix + "/" + f).filter(f => fs.lstatSync(f).isDirectory() ) 20 | const cfg = { 21 | // eslint-disable-next-line @typescript-eslint/no-var-requires 22 | //plugins: [autoImport], 23 | test: { 24 | include: ["./test/**/*.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], 25 | reporters: "verbose", 26 | globals: true 27 | }, 28 | resolve: { 29 | alias: packages.map(pkg => ({ pkg, json: pkg + "/package.json"})).filter(_ => fs.existsSync(_.json)).reduce((acc, { pkg, json}) => { 30 | acc[JSON.parse(fs.readFileSync(json, "utf-8")).name] = path.resolve(pkg, "src") 31 | return acc 32 | }, { }) // "effect-app/Prelude": path.join(__dirname, "packages/core/src/Prelude.code.ts") 33 | } 34 | } 35 | //console.log(cfg) 36 | return cfg 37 | } 38 | -------------------------------------------------------------------------------- /vitest.workspace.ts: -------------------------------------------------------------------------------- 1 | import * as path from "node:path" 2 | import { defineWorkspace, type UserWorkspaceConfig } from "vitest/config" 3 | 4 | // Remaining issues: 5 | // - Random failures (browser): https://github.com/vitest-dev/vitest/issues/4497 6 | // - Alias resolution (browser, has workaround): https://github.com/vitest-dev/vitest/issues/4744 7 | // - Workspace optimization: https://github.com/vitest-dev/vitest/issues/4746 8 | 9 | // TODO: Once https://github.com/vitest-dev/vitest/issues/4497 and https://github.com/vitest-dev/vitest/issues/4746 10 | // are resolved, we can create specialized workspace groups in separate workspace files to better control test groups 11 | // with different dependencies (e.g. playwright browser) in CI. 12 | 13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 14 | const project = ( 15 | config: UserWorkspaceConfig["test"] & { name: `${string}|${string}` }, 16 | root = config.root ?? path.join(__dirname, `packages/${config.name.split("|").at(0)}`) 17 | ) => ({ 18 | // extends: "vitest.shared.ts", 19 | test: { root, ...config } 20 | }) 21 | 22 | export default defineWorkspace([ 23 | // Add specialized configuration for some packages. 24 | // project({ name: "effect|browser", environment: "happy-dom" }), 25 | // project({ name: "schema|browser", environment: "happy-dom" }), 26 | // Add the default configuration for all packages. 27 | "packages/*" 28 | ]) 29 | -------------------------------------------------------------------------------- /wallaby.base.cjs: -------------------------------------------------------------------------------- 1 | module.exports = () => { 2 | return { 3 | autoDetect: true, 4 | } 5 | } --------------------------------------------------------------------------------