Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 0 additions & 33 deletions .github/dependabot.yml

This file was deleted.

6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@ vitest.config.*.timestamp*

vendor
vendor/effect

.claude/worktrees
.claude/settings.local.json
.nx/polygraph
.nx/self-healing
.nx/migrate-runs
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ pnpm-lock.yaml
.claude
.cursor
.agents

.nx/self-healing
*.gen.ts
4 changes: 2 additions & 2 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ Effect v4 renames several built-in error types. These appear in the error channe
// Before (v3)
repo.getById(id).pipe(
Effect.catchTag('NoSuchElementException', () => Effect.succeed(null)),
Effect.catchTag('ParseError', (e) => Effect.fail(new MyError({ cause: e })))
Effect.catchTag('ParseError', (e) => Effect.fail(new MyError({ cause: e }))),
);

// After (v4)
repo.getById(id).pipe(
Effect.catchTag('NoSuchElementError', () => Effect.succeed(null)),
Effect.catchTag('SchemaError', (e) => Effect.fail(new MyError({ cause: e })))
Effect.catchTag('SchemaError', (e) => Effect.fail(new MyError({ cause: e }))),
);
```

Expand Down
26 changes: 16 additions & 10 deletions REACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ export const firestoreLayerAtom = Atom.keepAlive(
// forgotten seed fails loudly instead of being silenced by a cast.
Layer.effect(
FirestoreService,
Effect.die('firestoreLayerAtom must be seeded via RegistryProvider initialValues'),
Effect.die(
'firestoreLayerAtom must be seeded via RegistryProvider initialValues',
),
),
),
);
Expand Down Expand Up @@ -120,9 +122,7 @@ export const postByIdAtom = Atom.family((id: typeof PostId.Type) =>
// back-navigation) without leaking one listener per visited post.
export const postByIdLiveAtom = Atom.family((id: typeof PostId.Type) =>
clientRuntime
.atom(
Stream.unwrap(Effect.map(PostRepository, (r) => r.getByIdStream(id))),
)
.atom(Stream.unwrap(Effect.map(PostRepository, (r) => r.getByIdStream(id))))
.pipe(Atom.setIdleTTL('30 seconds')),
);

Expand Down Expand Up @@ -164,7 +164,7 @@ Notes:
side with `reactivityKeys` on mutations: a completed mutation re-runs every
read that shares a key.
- **Concurrency:** without `concurrent: true`, a second invocation of an fn
atom *interrupts* the in-flight previous one (latest-wins) and all pending
atom _interrupts_ the in-flight previous one (latest-wins) and all pending
promise-mode awaiters resolve with the last invocation's result. That
default suits search-as-you-type reads; for mutations it can silently drop
a write, so pass `concurrent: true`.
Expand All @@ -184,9 +184,15 @@ function PostList() {
.onInitial(() => <Spinner />)
.onFailure((cause) => <Error message={Cause.pretty(cause)} />)
.onSuccess((posts) =>
posts.length === 0
? <Empty />
: <>{posts.map((p) => <PostCard key={p.id} post={p} />)}</>,
posts.length === 0 ? (
<Empty />
) : (
<>
{posts.map((p) => (
<PostCard key={p.id} post={p} />
))}
</>
),
)
.exhaustive();
}
Expand Down Expand Up @@ -219,7 +225,7 @@ function CreatePost() {
const create = useAtomSet(addPostAtom, { mode: 'promise' });
return (
<Button
onClick={() => create({ title: 'Hello', /* ... */ }).catch(/* ... */)}
onClick={() => create({ title: 'Hello' /* ... */ }).catch(/* ... */)}
>
Create
</Button>
Expand Down Expand Up @@ -266,7 +272,7 @@ function PostForm() {
onSubmit: async ({ value }) => {
setSubmitError(null);
try {
await create({ ...value, /* fill required fields */ });
await create({ ...value /* fill required fields */ });
form.reset();
} catch {
// form-core rethrows onSubmit errors out of handleSubmit, so an
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ export const PostRepository = Model.makeRepository(PostModel, {
repo.queryStream(
Query.and(
Query.where('status', '==', 'published'),
Query.orderBy('createdAt', 'desc')
)
Query.orderBy('createdAt', 'desc'),
),
),
}))
})),
);
```

Expand All @@ -99,8 +99,8 @@ const program = Effect.gen(function* () {
}).pipe(
Effect.provide(PostRepository),
Effect.provide(
Client.layer({ app: initializeApp({ projectId: 'my-project' }) })
)
Client.layer({ app: initializeApp({ projectId: 'my-project' }) }),
),
);
```

Expand All @@ -116,15 +116,15 @@ Firestore.withTransaction(
const repo = yield* PostRepository;
const post = yield* repo.getById(postId);
yield* repo.update(postId, { status: 'published' });
})
}),
);

// Stage many writes and commit them atomically
Firestore.withBatch(
Effect.gen(function* () {
const repo = yield* PostRepository;
yield* Effect.forEach(ids, (id) => repo.update(id, { status: 'archived' }));
})
}),
);
```

Expand All @@ -136,7 +136,7 @@ import { initializeApp } from 'firebase-admin/app';
import { Admin, FunctionsRuntime, onCallEffect } from '@effect-firebase/admin';

const runtime = FunctionsRuntime.make(
Layer.mergeAll(Admin.layer({ app: initializeApp() }), PostRepository)
Layer.mergeAll(Admin.layer({ app: initializeApp() }), PostRepository),
);

export const createPost = onCallEffect({ runtime }, (request) =>
Expand All @@ -149,7 +149,7 @@ export const createPost = onCallEffect({ runtime }, (request) =>
status: 'draft',
});
return { postId };
})
}),
);
```

Expand All @@ -169,7 +169,7 @@ await Effect.runPromise(
});
const post = yield* repo.getById(postId);
expect(post.title).toBe('Test');
}).pipe(Effect.provide(PostRepository), Effect.provide(mockFirestore))
}).pipe(Effect.provide(PostRepository), Effect.provide(mockFirestore)),
);
```

Expand Down
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import nx from '@nx/eslint-plugin';
import jsoncParser from 'jsonc-eslint-parser';
import * as jsoncParser from 'jsonc-eslint-parser';

export default [
...nx.configs['flat/base'],
Expand Down
2 changes: 1 addition & 1 deletion example/app/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
Expand Down
22 changes: 11 additions & 11 deletions example/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,24 @@
"@effect/atom-react": "catalog:",
"@effect/platform-browser": "catalog:",
"@example/shared": "workspace:*",
"@nx/react": "22.4.5",
"@nx/vite": "22.5.4",
"@tanstack/react-form": "^1.32.0",
"@tanstack/react-router": "^1.139.3",
"@tanstack/react-router-devtools": "^1.139.3",
"@tanstack/router-plugin": "^1.139.3",
"@vitejs/plugin-react": "^4.2.0",
"@nx/vite": "23.1.1",
"@tailwindcss/vite": "^4.3.3",
"@tanstack/react-form": "^1.33.3",
"@tanstack/react-router": "^1.170.19",
"@tanstack/react-router-devtools": "^1.167.1",
"@tanstack/router-plugin": "^1.168.24",
"@vitejs/plugin-react": "6.0.5",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"effect": "catalog:",
"effect-firebase": "workspace:*",
"firebase": "catalog:",
"react": "19.2.4",
"react-dom": "19.2.4",
"tailwind-merge": "^3.4.0"
"react": "19.2.8",
"react-dom": "19.2.8",
"tailwind-merge": "^3.6.0"
},
"devDependencies": {
"@effect-firebase/mock": "workspace:*",
"vite": "7.1.8"
"vite": "8.2.0"
}
}
15 changes: 0 additions & 15 deletions example/app/postcss.config.js

This file was deleted.

9 changes: 5 additions & 4 deletions example/app/src/components/core/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,20 @@ const buttonVariants = cva(
variant: 'primary',
size: 'md',
},
}
},
);

export interface ButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement>,
extends
ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
isLoading?: boolean;
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(
{ className, variant, size, isLoading, disabled, children, ...props },
ref
ref,
) => {
return (
<button
Expand Down Expand Up @@ -94,7 +95,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
)}
</button>
);
}
},
);

Button.displayName = 'Button';
17 changes: 9 additions & 8 deletions example/app/src/components/core/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ const cardVariants = cva('rounded-lg border p-4', {
});

export interface CardProps
extends HTMLAttributes<HTMLDivElement>,
VariantProps<typeof cardVariants> {}
extends HTMLAttributes<HTMLDivElement>, VariantProps<typeof cardVariants> {}

export const Card = forwardRef<HTMLDivElement, CardProps>(
({ className, variant, ...props }, ref) => {
Expand All @@ -47,7 +46,7 @@ export const Card = forwardRef<HTMLDivElement, CardProps>(
{...props}
/>
);
}
},
);

Card.displayName = 'Card';
Expand All @@ -69,7 +68,8 @@ const cardHeaderVariants = cva('mb-2', {
});

export interface CardHeaderProps
extends HTMLAttributes<HTMLHeadingElement>,
extends
HTMLAttributes<HTMLHeadingElement>,
VariantProps<typeof cardHeaderVariants> {}

export const CardHeader = forwardRef<HTMLHeadingElement, CardHeaderProps>(
Expand All @@ -80,12 +80,12 @@ export const CardHeader = forwardRef<HTMLHeadingElement, CardHeaderProps>(
className={cn(
'text-xl font-semibold',
cardHeaderVariants({ variant }),
className
className,
)}
{...props}
/>
);
}
},
);

CardHeader.displayName = 'CardHeader';
Expand All @@ -107,7 +107,8 @@ const cardContentVariants = cva('', {
});

export interface CardContentProps
extends HTMLAttributes<HTMLParagraphElement>,
extends
HTMLAttributes<HTMLParagraphElement>,
VariantProps<typeof cardContentVariants> {}

export const CardContent = forwardRef<HTMLParagraphElement, CardContentProps>(
Expand All @@ -119,7 +120,7 @@ export const CardContent = forwardRef<HTMLParagraphElement, CardContentProps>(
{...props}
/>
);
}
},
);

CardContent.displayName = 'CardContent';
4 changes: 2 additions & 2 deletions example/app/src/components/core/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
id={inputId}
className={cn(
'h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500',
className
className,
)}
{...props}
/>
Expand All @@ -47,7 +47,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
)}
</div>
);
}
},
);

Checkbox.displayName = 'Checkbox';
Loading
Loading