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
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ The recommended application API is registry-based:

```ts
import { z } from 'zod'
import { defineFirestate, doc, col } from '@hvakr/firestate'
import { createFirestate, doc, col } from '@hvakr/firestate'

const TaskListSchema = z.object({ name: z.string(), createdAt: z.number() })
const TaskSchema = z.object({
title: z.string(),
completed: z.boolean(),
})

export const { useTaskList, useTasks } = defineFirestate({
export const { useTaskList, useTasks } = createFirestate({
taskList: doc({ path: 'taskLists/{listId}', schema: TaskListSchema }),
tasks: col({ path: 'taskLists/{listId}/tasks', schema: TaskSchema }),
})
Expand All @@ -47,7 +47,7 @@ CI runs `pnpm typecheck`, `pnpm build`, and `pnpm test` on Node 22.
## Source Map

- `src/index.ts` - public exports. Update this when adding public API.
- `src/firestate.ts` - registry API: `defineFirestate`, `doc`, `col`, path
- `src/firestate.ts` - registry API: `createFirestate`, `doc`, `col`, path
template validation, generated hook typing.
- `src/schema.ts` - lower-level definition helpers.
- `src/types.ts` - public state, handle, definition, undo, and config types.
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pnpm test:coverage

## Change Guidelines

- Prefer the registry API (`defineFirestate`, `doc`, `col`) for examples and
- Prefer the registry API (`createFirestate`, `doc`, `col`) for examples and
app-facing docs.
- Keep the lower-level API (`defineDocument`, `defineCollection`,
`useDocument`, `useCollection`) working as an escape hatch.
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Firestate provides a declarative, schema-first approach that eliminates boilerpl

Firestate exposes two layers. Pick one based on what you're building:

- **`defineFirestate` + `doc` / `col`** (recommended for app code) — declare every Firestore thing in a single registry object; the library generates one typed React hook per entry. Each entry takes a `path` template and a Zod `schema`. In return you get:
- **`createFirestate` + `doc` / `col`** (recommended for app code) — declare every Firestore thing in a single registry object; the library generates one typed React hook per entry. Each entry takes a `path` template and a Zod `schema`. In return you get:
- the data type (`TaskList`) inferred from the schema via `z.infer`
- the param keys (`{ listId }`) inferred from the path template and enforced at call sites
- runtime validation on `set` / `add` writes — bad data throws at the call site instead of after a Firestore round trip
Expand All @@ -40,12 +40,12 @@ Firestate exposes two layers. Pick one based on what you're building:

```ts
import { z } from 'zod'
import { defineFirestate, doc, col } from '@hvakr/firestate'
import { createFirestate, doc, col } from '@hvakr/firestate'

const TaskListSchema = z.object({ name: z.string(), createdAt: z.number() })
const TaskSchema = z.object({ title: z.string(), completed: z.boolean() })

export const { useTaskList, useTasks } = defineFirestate({
export const { useTaskList, useTasks } = createFirestate({
taskList: doc({ path: 'taskLists/{listId}', schema: TaskListSchema }),
tasks: col({ path: 'taskLists/{listId}/tasks', schema: TaskSchema }),
})
Expand Down Expand Up @@ -370,14 +370,14 @@ awaiting writes is not feasible.

### Registry API

#### `defineFirestate(registry)`
#### `createFirestate(registry)`

Creates typed React hooks from a registry object. Each key becomes a hook named
`use{CapitalizedKey}`.

```typescript
import { z } from 'zod'
import { defineFirestate, doc, col } from '@hvakr/firestate'
import { createFirestate, doc, col } from '@hvakr/firestate'

const ProjectSchema = z.object({
name: z.string(),
Expand All @@ -390,7 +390,7 @@ const SpaceSchema = z.object({
floor: z.number(),
})

export const { useProject, useSpaces } = defineFirestate({
export const { useProject, useSpaces } = createFirestate({
project: doc({
path: 'projects/{projectId}',
schema: ProjectSchema,
Expand Down
6 changes: 3 additions & 3 deletions docs/api-recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ modifying Firestate.

## Recommended Registry API

Use `defineFirestate`, `doc`, and `col` for normal app code.
Use `createFirestate`, `doc`, and `col` for normal app code.

```ts
import { z } from 'zod'
import { defineFirestate, doc, col } from '@hvakr/firestate'
import { createFirestate, doc, col } from '@hvakr/firestate'

const ProjectSchema = z.object({
name: z.string(),
Expand All @@ -22,7 +22,7 @@ const SpaceSchema = z.object({
floor: z.number(),
})

export const { useProject, useSpaces } = defineFirestate({
export const { useProject, useSpaces } = createFirestate({
project: doc({
path: 'projects/{projectId}',
schema: ProjectSchema,
Expand Down
4 changes: 2 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Firestate has two public API layers over the same core subscription system.

The recommended layer is the registry API:

- `defineFirestate(registry)` creates named React hooks.
- `createFirestate(registry)` creates named React hooks.
- `doc({ path, schema })` declares one document entry.
- `col({ path, schema })` declares one collection entry.

Expand All @@ -24,7 +24,7 @@ At runtime, a typical React app follows this flow:

1. The app renders `FirestateProvider` with a Firestore instance.
2. `FirestateProvider` creates a `FirestateStore`.
3. A generated hook from `defineFirestate`, or a direct call to `useDocument`
3. A generated hook from `createFirestate`, or a direct call to `useDocument`
/ `useCollection`, resolves the Firestore path from params.
4. The hook creates a document or collection subscription.
5. The subscription attaches an `onSnapshot` listener when loaded.
Expand Down
2 changes: 1 addition & 1 deletion examples/react-tasks/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const LIST_ID = 'demo-list'
function TaskListEditor() {
const params = { listId: LIST_ID }

// The two hooks below come straight from `defineFirestate(...)` in
// The two hooks below come straight from `createFirestate(...)` in
// schemas.ts — one registry, generated `useTaskList` / `useTasks`.
const taskList = useTaskList(params)
const tasks = useTasks(params)
Expand Down
4 changes: 2 additions & 2 deletions examples/react-tasks/src/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineFirestate, doc, col } from '@hvakr/firestate'
import { createFirestate, doc, col } from '@hvakr/firestate'
import { TaskListSchema, TaskSchema } from './types'

export const { useTaskList, useTasks } = defineFirestate({
export const { useTaskList, useTasks } = createFirestate({
taskList: doc({
path: 'taskLists/{listId}',
schema: TaskListSchema,
Expand Down
14 changes: 7 additions & 7 deletions src/firestate.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, it, expect } from 'vitest'
import { z } from 'zod'
import {
defineFirestate,
createFirestate,
doc,
col,
interpolatePath,
Expand Down Expand Up @@ -138,9 +138,9 @@ describe('splitDocPath', () => {
})
})

describe('defineFirestate', () => {
describe('createFirestate', () => {
it('produces a hook per registry key', () => {
const api = defineFirestate({
const api = createFirestate({
taskList: doc({ path: 'taskLists/{listId}', schema: tlSchema }),
tasks: col({
path: 'taskLists/{listId}/tasks',
Expand All @@ -153,7 +153,7 @@ describe('defineFirestate', () => {
})

it('capitalizes the first character of each key', () => {
const api = defineFirestate({
const api = createFirestate({
a: doc({ path: 'a/{id}', schema: tlSchema }),
longerName: col({ path: 'a/{id}/sub', schema: taskSchema }),
})
Expand All @@ -164,13 +164,13 @@ describe('defineFirestate', () => {

it('rejects invalid keys', () => {
expect(() =>
defineFirestate({
createFirestate({
'1bad': doc({ path: 'a/{id}', schema: tlSchema }),
} as any)
).toThrow(/must start with a letter/)

expect(() =>
defineFirestate({
createFirestate({
'bad-key': doc({ path: 'a/{id}', schema: tlSchema }),
} as any)
).toThrow(/must start with a letter/)
Expand Down Expand Up @@ -215,7 +215,7 @@ describe('type-level params extraction', () => {
// "is this call signature actually enforced?".
it('requires the right param keys at call sites', () => {
function _typeTest() {
const api = defineFirestate({
const api = createFirestate({
taskList: doc({
path: 'taskLists/{listId}',
schema: tlSchema,
Expand Down
10 changes: 5 additions & 5 deletions src/firestate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* interface TaskList { name: string; createdAt: number }
* interface Task { title: string; completed: boolean }
*
* export const { useTaskList, useTasks } = defineFirestate({
* export const { useTaskList, useTasks } = createFirestate({
* taskList: doc<TaskList>('taskLists/{listId}'),
* tasks: col<Task>('taskLists/{listId}/tasks'),
* })
Expand Down Expand Up @@ -223,7 +223,7 @@ export function col<
}

// ---------------------------------------------------------------------------
// defineFirestate
// createFirestate
// ---------------------------------------------------------------------------

type HookName<K extends string> = `use${Capitalize<K>}`;
Expand Down Expand Up @@ -262,13 +262,13 @@ export type FirestateApi<R extends FirestateRegistry> = {
* `K` produces a hook named `use{Capitalize<K>}`.
*
* ```ts
* export const { useTaskList, useTasks } = defineFirestate({
* export const { useTaskList, useTasks } = createFirestate({
* taskList: doc<TaskList>('taskLists/{listId}'),
* tasks: col<Task>('taskLists/{listId}/tasks'),
* })
* ```
*/
export function defineFirestate<R extends FirestateRegistry>(
export function createFirestate<R extends FirestateRegistry>(
registry: R
): FirestateApi<R> {
const api: Record<string, unknown> = {};
Expand Down Expand Up @@ -303,7 +303,7 @@ export function defineFirestate<R extends FirestateRegistry>(
/**
* Build the underlying {@link DocumentDefinition} for a registry doc entry.
* Exported for unit testing — registry consumers should call
* {@link defineFirestate} instead.
* {@link createFirestate} instead.
*
* @internal
*/
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export type {
} from './schema'

// Registry-driven API
export { defineFirestate, doc, col } from './firestate'
export { createFirestate, doc, col } from './firestate'

export type {
DocEntry,
Expand Down
4 changes: 2 additions & 2 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
/**
* Define a typed document. `TData` is the document's TypeScript shape.
*
* **Most apps should reach for {@link defineFirestate} + {@link doc} instead**
* **Most apps should reach for {@link createFirestate} + {@link doc} instead**
* — that builds a registry of every Firestore thing in one object and
* generates typed hooks for you. `defineDocument` is the lower-level
* escape hatch: use it when you need fully custom `collection` / `id`
Expand Down Expand Up @@ -61,7 +61,7 @@ export function defineDocument(
* Define a typed collection. `TData` is the shape of each document in the
* collection. See {@link defineDocument} for the schema/plain-type tradeoff.
*
* **Most apps should reach for {@link defineFirestate} + {@link col} instead.**
* **Most apps should reach for {@link createFirestate} + {@link col} instead.**
* `defineCollection` is the escape hatch for fully custom path derivation
* or non-React usage.
*
Expand Down
Loading