You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
> This project is under active development. Expect breaking changes before v1.0.
16
16
17
-
firecast is a thin wrapper over Firestore's SDKs, giving both one schema-typed interface: `firebase-admin` on the server and `firebase` on the web. Define a collection's schema once, in a validator you already use. Reads come back coerced and typed, and queries are typed to its fields. Underneath it's still plain Firestore, so you can reach for the SDK whenever you like.
17
+
kilncast is a thin wrapper over Firestore's SDKs, giving both one schema-typed interface: `firebase-admin` on the server and `firebase` on the web. Define a collection's schema once, in a validator you already use. Reads come back coerced and typed, and queries are typed to its fields. Underneath it's still plain Firestore, so you can reach for the SDK whenever you like.
firecast moves all of this onto the collection definition, built from a schema you already have.
48
+
kilncast moves all of this onto the collection definition, built from a schema you already have.
49
49
50
-
The schema is a [Standard Schema](https://standardschema.dev) validator, so Zod, Valibot, ArkType or any other compliant library supplies the types. firecast depends only on the spec's type definitions and never runs your schema; there is no runtime validation unless you want some, and then you run the same schema yourself.
50
+
The schema is a [Standard Schema](https://standardschema.dev) validator, so Zod, Valibot, ArkType or any other compliant library supplies the types. kilncast depends only on the spec's type definitions and never runs your schema; there is no runtime validation unless you want some, and then you run the same schema yourself.
51
51
52
52
Timestamps round-trip. Write a `Date`, read a `Date`, however deeply it sits in maps and arrays. And one definition covers both SDKs: the same typed surface works with `firebase-admin` on the server and `firebase` on the web, so the model code you used to duplicate lives in one module, and that module imports no Firebase.
53
53
54
54
The whole surface is typed against the schema: reads and writes, the query builder and its aggregations, listeners, transactions, subcollections and collection-group queries. A misspelt field is a compile error rather than an empty result.
55
55
56
-
Underneath, it is still plain Firestore. firecast is a thin wrapper, not an ORM, and every handle exposes `.ref`, so you can always drop to the raw SDK.
56
+
Underneath, it is still plain Firestore. kilncast is a thin wrapper, not an ORM, and every handle exposes `.ref`, so you can always drop to the raw SDK.
57
57
58
58
## Install
59
59
60
60
```sh
61
-
npm install firecast
61
+
npm install kilncast
62
62
# plus whichever SDK you use
63
63
npm install firebase-admin # server
64
64
npm install firebase # web
@@ -69,7 +69,7 @@ npm install firebase # web
69
69
Define a collection, connect a database, then read and write typed documents.
@@ -98,12 +98,12 @@ const post = await db.collection(posts).get("hello-world");
98
98
99
99
## Define a schema
100
100
101
-
A collection definition is a plain value (`name` + `schema`) with no database binding, so it's reusable at any path, including subcollections. The schema module imports only `firecast`, so Firebase never reaches a frontend bundle.
101
+
A collection definition is a plain value (`name` + `schema`) with no database binding, so it's reusable at any path, including subcollections. The schema module imports only `kilncast`, so Firebase never reaches a frontend bundle.
102
102
103
-
The schema should describe a document Firestore can store: an object of fields, with no directly nested arrays (an array of arrays). firecast does not police this, so a non-storable shape surfaces as an SDK error at write time rather than a firecast one.
103
+
The schema should describe a document Firestore can store: an object of fields, with no directly nested arrays (an array of arrays). kilncast does not police this, so a non-storable shape surfaces as an SDK error at write time rather than a kilncast one.
104
104
105
105
```ts
106
-
import { collection } from"firecast";
106
+
import { collection } from"kilncast";
107
107
import { z } from"zod";
108
108
109
109
exportconst posts =collection(
@@ -123,7 +123,7 @@ Pass a Firestore instance to `createDatabase`. The server entrypoint uses `fireb
Dotted paths reach into nested maps, typed end to end. `where("customer.address.city", "==", "London")` requires that path to exist and its value to be a string. Paths stop at arrays and timestamps (those are queried as whole values), so `where("tags", "array-contains", "vip")` is valid but `where("tags.0", ...)` is not.
217
217
218
-
The document id is not a schema field, so target it with `documentId()` (imported from `firecast`). Use it to filter by id, or as an ordering tiebreak. Id values are plain strings, not the schema's field types.
218
+
The document id is not a schema field, so target it with `documentId()` (imported from `kilncast`). Use it to filter by id, or as an ordering tiebreak. Id values are plain strings, not the schema's field types.
219
219
220
220
```ts
221
-
import { documentId } from"firecast";
221
+
import { documentId } from"kilncast";
222
222
223
223
const some =awaitdb
224
224
.collection(posts)
@@ -312,15 +312,15 @@ It is the right tool for blind atomic writes. A transaction would cover these to
312
312
313
313
## What's guaranteed
314
314
315
-
firecast types and coerces. It does not validate.
315
+
kilncast types and coerces. It does not validate.
316
316
317
-
Reads are coerced and typed. A read coerces stored Firestore values to neutral types (every `Timestamp` becomes a `Date`), then merges the document id in flat as `(T & { id }) | null`. firecast never runs your schema, so a document that has drifted from it still comes back, typed as valid. Validate on read yourself where that matters.
317
+
Reads are coerced and typed. A read coerces stored Firestore values to neutral types (every `Timestamp` becomes a `Date`), then merges the document id in flat as `(T & { id }) | null`. kilncast never runs your schema, so a document that has drifted from it still comes back, typed as valid. Validate on read yourself where that matters.
318
318
319
319
Writes are coerced. `set`, `add`, `update` and merge `set` coerce `Date` to `Timestamp` and translate sentinels, then write. The typed surface constrains every field at compile time, but nothing is checked at runtime.
320
320
321
321
### Sentinels
322
322
323
-
firecast provides its own sentinels (`serverTimestamp`, `increment`, `arrayUnion`, `arrayRemove`, `deleteField`) because a neutral schema can't reference the admin or web `FieldValue` class. Each driver translates them to its own SDK at write time.
323
+
kilncast provides its own sentinels (`serverTimestamp`, `increment`, `arrayUnion`, `arrayRemove`, `deleteField`) because a neutral schema can't reference the admin or web `FieldValue` class. Each driver translates them to its own SDK at write time.
324
324
325
325
In `update` and merge `set`, each sentinel is constrained to the field types it fits. `increment` works only on a number field, `arrayUnion` / `arrayRemove` only on a matching array, `serverTimestamp` only on a `Date` or `Timestamp` field, and `deleteField` only on an optional field. A mismatch is a compile error.
326
326
@@ -354,7 +354,7 @@ Schemas speak `Date`. The boundary coerces between `Date` and `Timestamp` deeply
354
354
If a field needs full nanosecond precision, keep it raw with the `raw` option (a list of dotted field paths). Those paths return the raw SDK value uncoerced on read, whatever the type, so your schema types them as the SDK type (`Timestamp` here) rather than a `Date`. The same option keeps any other field raw too, for example an SDK `Bytes` instead of a coerced `Uint8Array`.
Bytes are the binary analogue of timestamps. Schemas speak `Uint8Array`, the JS-native binary type, with no SDK import. The boundary coerces it to the SDK bytes type on write (the web `Bytes` class, an admin `Buffer`) and back to a plain `Uint8Array` on read, deeply, the same as `Date` and `Timestamp`. Lossless, so no precision caveat.
A schema can name Firestore's other value types without importing either SDK. firecast ships structural `GeoPoint`, `DocumentReference` and `VectorValue` interfaces that mirror neutral `Timestamp`. They are types only: firecast does not coerce them. They round-trip uncoerced as the SDK class instance you read and write.
389
+
A schema can name Firestore's other value types without importing either SDK. kilncast ships structural `GeoPoint`, `DocumentReference` and `VectorValue` interfaces that mirror neutral `Timestamp`. They are types only: kilncast does not coerce them. They round-trip uncoerced as the SDK class instance you read and write.
firecast does not validate, so it does not catch drift. Stored data can diverge from the current schema: legacy docs, partial migrations, edits from other services or the console. A drifted document comes back coerced and typed as valid rather than throwing. Where that matters, run your schema on the result yourself, or drop to the SDK via `.ref`.
407
+
kilncast does not validate, so it does not catch drift. Stored data can diverge from the current schema: legacy docs, partial migrations, edits from other services or the console. A drifted document comes back coerced and typed as valid rather than throwing. Where that matters, run your schema on the result yourself, or drop to the SDK via `.ref`.
408
408
409
409
## Escape hatch
410
410
411
-
Every handle exposes a `.ref` with the converter attached, so raw Firestore calls that firecast doesn't wrap still run through it where the SDK invokes it. The converter coerces both directions: a read coerces stored values to neutral types and merges the id in, a write coerces `Date` to `Timestamp` and translates sentinels.
411
+
Every handle exposes a `.ref` with the converter attached, so raw Firestore calls that kilncast doesn't wrap still run through it where the SDK invokes it. The converter coerces both directions: a read coerces stored values to neutral types and merges the id in, a write coerces `Date` to `Timestamp` and translates sentinels.
412
412
413
413
`.ref` is typed `unknown` so the neutral core imports no SDK. Each entrypoint ships typed helpers, `docRef` / `collectionRef` / `queryRef`, that hand back the SDK ref typed to your schema, so you don't cast by hand.
// raw SDK call firecast does not wrap, data() is still coerced
422
+
// raw SDK call kilncast does not wrap, data() is still coerced
423
423
const unsub =ref.onSnapshot((snap) => {
424
424
// post: Doc<typeof posts.schema> | undefined
425
425
const post =snap.data();
@@ -433,5 +433,5 @@ If you need to drop fully to the raw SDK type yourself, `.ref` is still there to
433
433
434
434
## Errors
435
435
436
-
-`FirecastError` is the base class for the few errors firecast raises itself (such as an unknown sentinel). firecast polices neither ids nor write/query shapes, so those surface as the SDK's own error.
436
+
-`KilncastError` is the base class for the few errors kilncast raises itself (such as an unknown sentinel). kilncast polices neither ids nor write/query shapes, so those surface as the SDK's own error.
437
437
- Firestore, network, and permission errors propagate untouched.
0 commit comments