Skip to content

Commit 38a5f02

Browse files
committed
Introduce shared collections & groups
1 parent efa9dc1 commit 38a5f02

18 files changed

+237
-98
lines changed

Diff for: CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ This change log follows the format documented in [Keep a CHANGELOG].
77
[semantic versioning]: http://semver.org/
88
[keep a changelog]: http://keepachangelog.com/
99

10+
## v10.7.0 - 2024-04-23
11+
12+
### Added
13+
14+
- Added `as` method to collections and collections groups. The method allows sharing functionality between collections and collections groups. [Read the sharing functionality guide](https://typesaurus.com/type-safety/sharing/).
15+
16+
### Changed
17+
18+
- Deprecated `Typesaurus.Collection`, `Typesaurus.Ref`, and `Typesaurus.Doc` in favor of `Typesaurus.SharedCollection`, `Typesaurus.SharedRef`, and `Typesaurus.SharedDoc` to reflect the new sharing functionality. [Read the sharing functionality guide](https://typesaurus.com/type-safety/sharing/).
19+
1020
## v10.6.0 - 2024-04-12
1121

1222
**This release has no user-facing changes**, it's a dev release powering the [Typesaurus Point-in-Time Recovery adapter](https://github.com/kossnocorp/typesaurus-pitr).

Diff for: package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "typesaurus",
3-
"version": "10.6.0",
3+
"version": "10.7.0",
44
"description": "Type-safe ODM for Firestore",
55
"keywords": [
66
"Firebase",

Diff for: src/adapter/admin/core.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ export class Collection {
257257
firebaseDoc(id) {
258258
return this.firestore().doc(`${this.path}/${id}`);
259259
}
260+
261+
as() {
262+
return this;
263+
}
260264
}
261265

262266
export class Ref {

Diff for: src/adapter/admin/groups.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ class Group {
6060
return snap.data().result;
6161
}
6262

63+
as() {
64+
return this;
65+
}
66+
6367
adapter() {
6468
return {
6569
db: () => this.db,

Diff for: src/adapter/web/core.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ export class Collection {
269269
firebaseDoc(id) {
270270
return doc(this.firestore(), this.path, id);
271271
}
272+
273+
as() {
274+
return this;
275+
}
272276
}
273277

274278
export class Ref {

Diff for: src/adapter/web/groups.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ class Group {
7272
return snap.data().result;
7373
}
7474

75+
as() {
76+
return this;
77+
}
78+
7579
adapter() {
7680
return {
7781
db: () => this.db,

Diff for: src/index.ts

+18
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ export namespace Typesaurus {
102102
export type SharedCollection<Model extends Core.ModelObjectType> =
103103
Shared.Collection<Model>;
104104

105+
/**
106+
* Shared collection group type. Unlike regular group, shared group lacks
107+
* methods which type-safety depends on knowing the full type of the model:
108+
* `add`, `set`, `upset`, and `update`.
109+
*
110+
* [Learn more on the docs website](https://typesaurus.com/types/typesaurus/#sharedgroup)
111+
*/
112+
export type SharedGroup<Model extends Core.ModelObjectType> =
113+
Shared.Group<Model>;
114+
105115
/**
106116
* Concats models into single variable model type. Useful to define and export
107117
* variable models ouside of the centraliazed schema definition.
@@ -115,13 +125,17 @@ export namespace Typesaurus {
115125
* The type represents the document id.
116126
*
117127
* [Learn more on the docs website](https://typesaurus.com/types/typesaurus/#id)
128+
*
129+
* @deprecated Use `SharedRef` instead. Might be removed in the next major version.
118130
*/
119131
export type Id<Path extends string> = Core.Id<Path>;
120132

121133
/**
122134
* The type allows defining collection types.
123135
*
124136
* [Learn more on the docs website](https://typesaurus.com/types/typesaurus/#collection)
137+
*
138+
* @deprecated Use `SharedRef` instead. Might be removed in the next major version.
125139
*/
126140
export type Collection<
127141
Model extends Core.ModelType,
@@ -135,6 +149,8 @@ export namespace Typesaurus {
135149
* The type represents the document reference type.
136150
*
137151
* [Learn more on the docs website](https://typesaurus.com/types/typesaurus/#ref)
152+
*
153+
* @deprecated Use `SharedRef` instead. Might be removed in the next major version.
138154
*/
139155
export type Ref<
140156
Model extends Core.ModelType,
@@ -148,6 +164,8 @@ export namespace Typesaurus {
148164
* The type represents the document type.
149165
*
150166
* [Learn more on the docs website](https://typesaurus.com/types/typesaurus/#doc)
167+
*
168+
* @deprecated Use `SharedRef` instead. Might be removed in the next major version.
151169
*/
152170
export type Doc<
153171
Model extends Core.ModelType,

Diff for: src/tests/all.ts

+15-11
Original file line numberDiff line numberDiff line change
@@ -565,21 +565,25 @@ describe("all", () => {
565565
describe("request", () => {
566566
it("exposes the request", () => {
567567
const promise = db.books.all();
568-
expect(promise.request).toEqual({
569-
type: "request",
570-
kind: "all",
571-
path: "books",
572-
});
568+
expect(promise.request).toEqual(
569+
expect.objectContaining({
570+
type: "request",
571+
kind: "all",
572+
path: "books",
573+
}),
574+
);
573575
});
574576

575577
it("exposes a group request", () => {
576578
const promise = groups(db).books.all();
577-
expect(promise.request).toEqual({
578-
type: "request",
579-
kind: "all",
580-
path: "books",
581-
group: true,
582-
});
579+
expect(promise.request).toEqual(
580+
expect.objectContaining({
581+
type: "request",
582+
kind: "all",
583+
path: "books",
584+
group: true,
585+
}),
586+
);
583587
});
584588
});
585589
});

Diff for: src/tests/collection.ts

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from "vitest";
2-
import { schema } from "..";
2+
import { groups, schema } from "..";
33

44
describe("collection", () => {
55
it("allows to name the collection", async () => {
@@ -51,4 +51,24 @@ describe("collection", () => {
5151
expect(executions.name).toEqual("executions");
5252
expect(executions.path).toEqual("schedule/schedule-id/executions");
5353
});
54+
55+
describe("as", () => {
56+
const db = schema(($) => ({
57+
schedule: $.collection<any, string>(),
58+
nope: $.collection<any, string>().name("schedule"),
59+
}));
60+
61+
it("returns the collection", () => {
62+
const schedule = db.schedule.as();
63+
expect(schedule).toBe(db.schedule);
64+
});
65+
66+
describe("groups", () => {
67+
it("returns the collection", () => {
68+
const group = groups(db).schedule;
69+
const schedule = group.as();
70+
expect(schedule).toBe(group);
71+
});
72+
});
73+
});
5474
});

Diff for: src/tests/get.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -351,12 +351,14 @@ describe("get", () => {
351351
it("exposes the request", () => {
352352
const id = db.users.id("whatever");
353353
const promise = db.users.get(id);
354-
expect(promise.request).toEqual({
355-
type: "request",
356-
kind: "get",
357-
path: "users",
358-
id,
359-
});
354+
expect(promise.request).toEqual(
355+
expect.objectContaining({
356+
type: "request",
357+
kind: "get",
358+
path: "users",
359+
id,
360+
}),
361+
);
360362
});
361363
});
362364
});

Diff for: src/tests/many.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -328,12 +328,14 @@ describe("many", () => {
328328
db.fruits.id("apple"),
329329
db.fruits.id("banana"),
330330
]);
331-
expect(promise.request).toEqual({
332-
type: "request",
333-
kind: "many",
334-
path: "fruits",
335-
ids: [db.fruits.id("apple"), db.fruits.id("banana")],
336-
});
331+
expect(promise.request).toEqual(
332+
expect.objectContaining({
333+
type: "request",
334+
kind: "many",
335+
path: "fruits",
336+
ids: [db.fruits.id("apple"), db.fruits.id("banana")],
337+
}),
338+
);
337339
});
338340
});
339341
});

Diff for: src/tests/query.ts

+31-27
Original file line numberDiff line numberDiff line change
@@ -2242,39 +2242,43 @@ describe("query", () => {
22422242
describe("request", () => {
22432243
it("exposes the request", () => {
22442244
const promise = db.contacts.query(($) => $.field("ownerId").eq(ownerId));
2245-
expect(promise.request).toEqual({
2246-
type: "request",
2247-
kind: "query",
2248-
path: "contacts",
2249-
queries: [
2250-
{
2251-
field: ["ownerId"],
2252-
filter: "==",
2253-
type: "where",
2254-
value: ownerId,
2255-
},
2256-
],
2257-
});
2245+
expect(promise.request).toEqual(
2246+
expect.objectContaining({
2247+
type: "request",
2248+
kind: "query",
2249+
path: "contacts",
2250+
queries: [
2251+
{
2252+
field: ["ownerId"],
2253+
filter: "==",
2254+
type: "where",
2255+
value: ownerId,
2256+
},
2257+
],
2258+
}),
2259+
);
22582260
});
22592261

22602262
it("exposes a group request", () => {
22612263
const promise = groups(db).contacts.query(($) =>
22622264
$.field("ownerId").eq(ownerId),
22632265
);
2264-
expect(promise.request).toEqual({
2265-
type: "request",
2266-
kind: "query",
2267-
path: "contacts",
2268-
queries: [
2269-
{
2270-
field: ["ownerId"],
2271-
filter: "==",
2272-
type: "where",
2273-
value: ownerId,
2274-
},
2275-
],
2276-
group: true,
2277-
});
2266+
expect(promise.request).toEqual(
2267+
expect.objectContaining({
2268+
type: "request",
2269+
kind: "query",
2270+
path: "contacts",
2271+
queries: [
2272+
{
2273+
field: ["ownerId"],
2274+
filter: "==",
2275+
type: "where",
2276+
value: ownerId,
2277+
},
2278+
],
2279+
group: true,
2280+
}),
2281+
);
22782282
});
22792283
});
22802284
});

Diff for: src/types/core.ts

+10
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,16 @@ export namespace TypesaurusCore {
11371137
* const newCommentId = await db.comments.id()
11381138
*/
11391139
id(): Promise<Def["Id"]>;
1140+
1141+
/**
1142+
* The function narrows the type to shared collection type. Unlike regular
1143+
* collection, shared collection lacks methods which type-safety depends on
1144+
* knowing the full type of the model: `set`, `upset`, and `as`. The `ref`
1145+
* is also limited.
1146+
*
1147+
* When models don't match, it resolves `unknown`.
1148+
*/
1149+
as: Shared.CollectionAs<Def>;
11401150
}
11411151

11421152
export interface CollectionConfig {

Diff for: src/types/groups.ts

+11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { TypesaurusCore as Core } from "./core.js";
2+
import { TypesaurusShared as Shared } from "./shared.js";
23
import type { TypesaurusUtils as Utils } from "./utils.js";
34

45
export declare const groups: TypesaurusGroups.Function;
@@ -17,6 +18,16 @@ export namespace TypesaurusGroups {
1718

1819
/** The group name */
1920
name: string;
21+
22+
/**
23+
* The function narrows the type to shared group type. Unlike regular
24+
* group, shared group lacks methods which type-safety depends on
25+
* knowing the full type of the model: `set`, `upset`, and `as`. The `ref`
26+
* is also limited.
27+
*
28+
* When models don't match, it resolves `unknown`.
29+
*/
30+
as: Shared.GroupAs<Def>;
2031
}
2132

2233
/**

0 commit comments

Comments
 (0)