firetender 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # FireTender
2
+
3
+ FireTender is a wrapper for Firestore documents to make reading and writing them
4
+ simpler and safer. A Firestore doc looks like any other Typescript object, and
5
+ they are validated upon reading and writing.
6
+
7
+ Querying and concurrency are not yet supported. I'm adding features as I need
8
+ them, but contributions are most welcome. See the list of [alternative
9
+ packages](#alternatives) at the end of this README if you're looking for
10
+ something more mature.
11
+
12
+ ## Usage
13
+
14
+ To illustrate, let's run through the basics of defining, creating, reading, and
15
+ modifying a Firestore document.
16
+
17
+ ### Define your schemas
18
+
19
+ First, we define the document schemas and their validation criteria with
20
+ [Zod](https://github.com/colinhacks/zod). If you've used Joi or Yup, you will
21
+ find Zod very similar. Optional collections should use `.default({})` or
22
+ `.default([])` to simplify later access. Here we define a schema for types of
23
+ pizza, because I was hungry when I first wrote this.
24
+
25
+ Then create `pizzaWrapper`, which is a factory that makes objects to wrap
26
+ Firestore documents and enforce the given schema.
27
+
28
+ ```javascript
29
+ import { doc } from "firebase/firestore";
30
+ import { DocWrapper } from "firetender";
31
+ import { z } from "zod";
32
+
33
+ const pizzaSchema = z.object({
34
+ name: z.string(),
35
+ description: z.string().optional(),
36
+ toppings: z.record(
37
+ z.string(),
38
+ z.object({
39
+ isIncluded: z.boolean().default(true),
40
+ surcharge: z.number().positive().optional(),
41
+ placement: z.enum(["left", "right", "entire"]).default("entire"),
42
+ })
43
+ .refine((topping) => topping.isIncluded || topping.surcharge, {
44
+ message: "Toppings that are not included must have a surcharge.",
45
+ path: ["surcharge"],
46
+ })
47
+ ),
48
+ tags: z.array(z.string()).default([]),
49
+ });
50
+
51
+ const pizzaWrapper = new DocWrapper(pizzaSchema);
52
+ ```
53
+
54
+ ### Add a document
55
+
56
+ Let's add a document to the `pizzas` collection with an ID of `margherita`. We
57
+ use `DocWrapper.prototype.createNew()` to create a validated local object
58
+ representing a new document in the collection. We then add the doc to Firestore
59
+ by calling its `.write()` method.
60
+
61
+ ```javascript
62
+ const docRef = doc(db, "pizzas", "margherita");
63
+ const pizza = pizzaWrapper.createNew(docRef, {
64
+ name: "Margherita",
65
+ toppings: { "fresh mozzarella": {}, "fresh basil": {} },
66
+ tags: ["traditional"],
67
+ });
68
+ await pizza.write();
69
+ ```
70
+
71
+ If we don't care about the doc ID, we can also pass a collection reference
72
+ (e.g., `collection(db, "pizzas")`) to `.createNew()`. Firestore will assign a
73
+ random ID.
74
+
75
+ ### Read and modify a document
76
+
77
+ To read or modify an existing document, we instantiate a doc wrapper using
78
+ `DocWrapper.prototype.wrapExisting()`, passing in the doc's Firestore reference.
79
+ To read from it, we call `.load()` and access the data with `.ro` (read only);
80
+ to write, we modify the `.rw` accessor and then call `.write()`. They can be
81
+ used in combination, like so:
82
+
83
+ ```javascript
84
+ const meats = ["pepperoni", "chicken", "sausage"];
85
+ const pizza = await pizzaWrapper.wrapExisting(docRef).load();
86
+ const isMeatIncluded = Object.entries(pizza.ro.toppings).some(
87
+ ([name, topping]) => topping.isIncluded && name in meats
88
+ );
89
+ if (!isMeatIncluded) {
90
+ pizza.rw.toppings.tags.push("vegetarian");
91
+ }
92
+ await pizza.write();
93
+ ```
94
+
95
+ ### Make a copy
96
+
97
+ Here we create a new pizza in the same collection. Alternatively, a document
98
+ can be copied to elsewhere by specifying a document or collection reference.
99
+
100
+ ```javascript
101
+ const sourceRef = doc(db, "pizza", "margherita");
102
+ const sourcePizza = await pizzaWrapper.wrapExisting(sourceRef).load();
103
+ const newPizza = sourcePizza.copy("meaty margh");
104
+ newPizza.name = "Meaty Margh";
105
+ newPizza.toppings.sausage = {};
106
+ newPizza.toppings.pepperoni = { included: false, surcharge: 1.25 };
107
+ newPizza.toppings.chicken = { included: false, surcharge: 1.50 };
108
+ delete newPizza.toppings["fresh basil"];
109
+ delete newPizza.tags.vegetarian;
110
+ newPizza.write();
111
+ ```
112
+
113
+ ## TODO
114
+
115
+ * Concurrency
116
+ * Listen for changes and update the object if it has not been locally
117
+ modified. Provide an onChange() callback option.
118
+ * Support the Firestore transaction API.
119
+ * Queries
120
+ * Document deletion
121
+ * Improved timestamp handling
122
+ * Prod releases
123
+ * Block on failing tests
124
+ * Minify code (esbuild?)
125
+ * Release on github
126
+
127
+ ## Alternatives
128
+
129
+ This project is not at all stable yet. If you're looking for a more mature
130
+ Firestore helper, check out:
131
+
132
+ * [Vuefire](https://github.com/vuejs/vuefire) and
133
+ [Reactfire](https://github.com/FirebaseExtended/reactfire) for integration
134
+ with their respective frameworks.
135
+
136
+ * [Fireschema](https://github.com/yarnaimo/fireschema): Another strongly typed
137
+ framework for building and using schemas in Firestore.
138
+
139
+ * [firestore-fp](https://github.com/mobily/firestore-fp): If you are a
140
+ functional programming aficionado.
141
+
142
+ * [simplyfire](https://github.com/coturiv/simplyfire): A brilliantly named
143
+ simplified API that is focused more on querying.
144
+
145
+ I'm sure there are many more, and apologies if I missed your favorite.
@@ -0,0 +1,15 @@
1
+ import { CollectionReference, DocumentReference } from "firebase/firestore";
2
+ import { z } from "zod";
3
+ import { FireTenderDoc, FireTenderDocOptions } from "./FireTenderDoc";
4
+ /**
5
+ * Factory for FireTenderDoc objects based on the given schema.
6
+ */
7
+ export declare class DocWrapper<SchemaType extends z.SomeZodObject, DataType extends {
8
+ [x: string]: any;
9
+ } = z.infer<SchemaType>> {
10
+ private schema;
11
+ constructor(schema: SchemaType);
12
+ createNew(ref: DocumentReference | CollectionReference, initialData: any, // TODO: change to "DataType," after defaults are dropped.
13
+ options?: FireTenderDocOptions): FireTenderDoc<SchemaType, DataType>;
14
+ wrapExisting(ref: DocumentReference | CollectionReference, options?: FireTenderDocOptions): FireTenderDoc<SchemaType, DataType>;
15
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DocWrapper = void 0;
4
+ const FireTenderDoc_1 = require("./FireTenderDoc");
5
+ /**
6
+ * Factory for FireTenderDoc objects based on the given schema.
7
+ */
8
+ class DocWrapper {
9
+ constructor(schema) {
10
+ this.schema = schema;
11
+ }
12
+ createNew(ref, initialData, // TODO: change to "DataType," after defaults are dropped.
13
+ options = {}) {
14
+ const mergedOptions = {
15
+ ...options,
16
+ createDoc: true,
17
+ initialData,
18
+ };
19
+ return new FireTenderDoc_1.FireTenderDoc(this.schema, ref, mergedOptions);
20
+ }
21
+ wrapExisting(ref, options = {}) {
22
+ return new FireTenderDoc_1.FireTenderDoc(this.schema, ref, options);
23
+ }
24
+ }
25
+ exports.DocWrapper = DocWrapper;
26
+ //# sourceMappingURL=DocWrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocWrapper.js","sourceRoot":"","sources":["../src/DocWrapper.ts"],"names":[],"mappings":";;;AAEA,mDAAsE;AAEtE;;GAEG;AACH,MAAa,UAAU;IAErB,YAAoB,MAAkB;QAAlB,WAAM,GAAN,MAAM,CAAY;IAAG,CAAC;IAE1C,SAAS,CACP,GAA4C,EAC5C,WAAgB,EAAE,0DAA0D;IAC5E,UAAgC,EAAE;QAElC,MAAM,aAAa,GAAyB;YAC1C,GAAG,OAAO;YACV,SAAS,EAAE,IAAI;YACf,WAAW;SACZ,CAAC;QACF,OAAO,IAAI,6BAAa,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC;IAC5D,CAAC;IAED,YAAY,CACV,GAA4C,EAC5C,UAAgC,EAAE;QAElC,OAAO,IAAI,6BAAa,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;CACF;AAvBD,gCAuBC"}
@@ -0,0 +1,32 @@
1
+ import { CollectionReference, DocumentReference } from "firebase/firestore";
2
+ import { z } from "zod";
3
+ export declare type DeepReadonly<T> = T extends Array<infer ArrKey> ? ReadonlyArray<DeepReadonly<ArrKey>> : T extends Map<infer MapKey, infer MapVal> ? ReadonlyMap<DeepReadonly<MapKey>, DeepReadonly<MapVal>> : T extends Set<infer SetKey> ? ReadonlySet<DeepReadonly<SetKey>> : T extends Record<any, unknown> ? {
4
+ readonly [ObjKey in keyof T]: DeepReadonly<T[ObjKey]>;
5
+ } : T;
6
+ export declare type FireTenderDocOptions = {
7
+ createDoc?: true;
8
+ initialData?: any;
9
+ };
10
+ /**
11
+ * Helper class for reading and writing Firestore data based on Zod schemas.
12
+ */
13
+ export declare class FireTenderDoc<SchemaType extends z.SomeZodObject, DataType extends {
14
+ [x: string]: any;
15
+ } = z.infer<SchemaType>> {
16
+ readonly schema: SchemaType;
17
+ private ref;
18
+ private isNewDoc;
19
+ private docID;
20
+ private data;
21
+ private dataProxy;
22
+ private updates;
23
+ constructor(schema: SchemaType, ref: DocumentReference | CollectionReference, options?: FireTenderDocOptions);
24
+ get id(): string | undefined;
25
+ get docRef(): DocumentReference;
26
+ copy(dest?: DocumentReference | CollectionReference | string | undefined, options?: FireTenderDocOptions): FireTenderDoc<SchemaType, DataType>;
27
+ load(force?: boolean): Promise<this>;
28
+ get ro(): DeepReadonly<DataType>;
29
+ get rw(): DataType;
30
+ write(): Promise<void>;
31
+ private onChange;
32
+ }
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FireTenderDoc = void 0;
4
+ const firestore_1 = require("firebase/firestore");
5
+ const proxies_1 = require("./proxies");
6
+ function assertIsDefined(value) {
7
+ if (value === undefined || value === null) {
8
+ throw new TypeError(`${value} is not defined`);
9
+ }
10
+ }
11
+ /**
12
+ * Helper class for reading and writing Firestore data based on Zod schemas.
13
+ */
14
+ class FireTenderDoc {
15
+ constructor(schema, ref, options = {}) {
16
+ this.docID = undefined;
17
+ this.data = undefined;
18
+ this.dataProxy = undefined;
19
+ this.updates = new Map();
20
+ this.schema = schema;
21
+ this.ref = ref;
22
+ this.isNewDoc = options.createDoc ?? false;
23
+ console.log(ref, ref instanceof firestore_1.DocumentReference, options);
24
+ if (this.isNewDoc) {
25
+ if (!options.initialData) {
26
+ throw ReferenceError("Initial data must be given when creating a new doc.");
27
+ }
28
+ this.data = schema.parse(options.initialData);
29
+ }
30
+ if (this.ref.type === "document") {
31
+ this.docID = this.ref.path.split("/").pop();
32
+ }
33
+ else if (!this.isNewDoc) {
34
+ throw TypeError("FireTender can only take a collection reference when creating a new document. Use FireTender.createDoc() if this is your intent.");
35
+ }
36
+ }
37
+ get id() {
38
+ return this.docID;
39
+ }
40
+ get docRef() {
41
+ if (this.ref.type === "document") {
42
+ return this.ref;
43
+ }
44
+ throw Error("docRef can only be accessed after the new doc has been written.");
45
+ }
46
+ copy(dest = undefined, options = {}) {
47
+ if (!this.data) {
48
+ throw Error("You must call load() before making a copy.");
49
+ }
50
+ let ref;
51
+ if (dest && typeof dest !== "string") {
52
+ ref = dest;
53
+ }
54
+ else {
55
+ const collectionRef = this.ref.type === "document" ? this.ref.parent : this.ref;
56
+ if (dest) {
57
+ ref = (0, firestore_1.doc)(collectionRef, dest);
58
+ }
59
+ else {
60
+ ref = collectionRef;
61
+ }
62
+ }
63
+ const mergedOptions = {
64
+ ...options,
65
+ createDoc: true,
66
+ initialData: this.data,
67
+ };
68
+ return new FireTenderDoc(this.schema, ref, mergedOptions);
69
+ }
70
+ async load(force = false) {
71
+ if (this.isNewDoc || this.ref.type === "collection") {
72
+ throw Error("load() should not be called for new documents.");
73
+ }
74
+ if (!this.data || force) {
75
+ const snapshot = await (0, firestore_1.getDoc)(this.ref);
76
+ if (!snapshot.exists()) {
77
+ throw new Error("Document does not exist.");
78
+ }
79
+ this.data = this.schema.parse(snapshot.data());
80
+ // Dereference the old proxy, if any, to force a recapture of data.
81
+ this.dataProxy = undefined;
82
+ }
83
+ return this;
84
+ }
85
+ get ro() {
86
+ if (!this.data) {
87
+ throw Error("You must call load() before using the .ro accessor.");
88
+ }
89
+ return this.data;
90
+ }
91
+ get rw() {
92
+ if (this.isNewDoc) {
93
+ // No need to monitor changes if we're creating rather than updating.
94
+ return this.data;
95
+ }
96
+ if (!this.dataProxy) {
97
+ if (!this.data) {
98
+ throw Error("You must call load() before using the .rw accessor.");
99
+ }
100
+ this.dataProxy = (0, proxies_1.watchFieldForChanges)([], this.schema, this.data, this.onChange.bind(this));
101
+ }
102
+ return this.dataProxy;
103
+ }
104
+ async write() {
105
+ if (this.isNewDoc) {
106
+ assertIsDefined(this.data);
107
+ if (this.ref.type === "document") {
108
+ await (0, firestore_1.setDoc)(this.ref, this.data);
109
+ }
110
+ else {
111
+ this.ref = await (0, firestore_1.addDoc)(this.ref, this.data);
112
+ this.docID = this.ref.path.split("/").pop();
113
+ }
114
+ this.isNewDoc = false;
115
+ }
116
+ else {
117
+ if (!(this.ref.type === "document")) {
118
+ // We should never get here.
119
+ throw Error("Internal error. FireTender object should always reference a document when updating an existing doc.");
120
+ }
121
+ if (this.updates.size === 0) {
122
+ return;
123
+ }
124
+ const flatUpdateList = Array.from(this.updates.entries()).flat();
125
+ await (0, firestore_1.updateDoc)(this.ref, flatUpdateList[0], flatUpdateList[1], ...flatUpdateList.slice(2));
126
+ }
127
+ }
128
+ onChange(fieldPath, newValue) {
129
+ this.updates.set(fieldPath.join("."), newValue);
130
+ }
131
+ }
132
+ exports.FireTenderDoc = FireTenderDoc;
133
+ //# sourceMappingURL=FireTenderDoc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FireTenderDoc.js","sourceRoot":"","sources":["../src/FireTenderDoc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Timestamp representation used by Firestore: seconds and nanoseconds since the
4
+ * epoch.
5
+ */
6
+ export declare const timestampSchema: z.ZodObject<{
7
+ seconds: z.ZodNumber;
8
+ nanoseconds: z.ZodNumber;
9
+ }, "strip", z.ZodTypeAny, {
10
+ seconds: number;
11
+ nanoseconds: number;
12
+ }, {
13
+ seconds: number;
14
+ nanoseconds: number;
15
+ }>;
16
+ export declare type TimestampData = z.infer<typeof timestampSchema>;
17
+ export declare function dateFromTimestamp(timestamp: TimestampData): Date;
18
+ export declare function makeTTL(daysFromNow?: number): {
19
+ seconds: number;
20
+ nanoseconds: number;
21
+ };
22
+ export declare function timestampFromDate(date: Date): TimestampData;
23
+ export declare function timestampFromUnixMillis(msSinceEpoch: number): TimestampData;
24
+ export declare function nowTimestamp(): {
25
+ seconds: number;
26
+ nanoseconds: number;
27
+ };
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.nowTimestamp = exports.timestampFromUnixMillis = exports.timestampFromDate = exports.makeTTL = exports.dateFromTimestamp = exports.timestampSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Timestamp representation used by Firestore: seconds and nanoseconds since the
7
+ * epoch.
8
+ */
9
+ exports.timestampSchema = zod_1.z.object({
10
+ seconds: zod_1.z.number().positive().int(),
11
+ nanoseconds: zod_1.z.number().nonnegative().int(),
12
+ });
13
+ // TODO: probably want to move these timestamp functions into their own helper
14
+ // module
15
+ function dateFromTimestamp(timestamp) {
16
+ return new Date(timestamp.seconds * 1e3 + timestamp.nanoseconds / 1e6);
17
+ }
18
+ exports.dateFromTimestamp = dateFromTimestamp;
19
+ function makeTTL(daysFromNow = 30) {
20
+ // TODO: is there a way to use the server time rather than Date.now()?
21
+ return timestampFromUnixMillis(Date.now() + daysFromNow * 24 * 60 * 60 * 1000);
22
+ }
23
+ exports.makeTTL = makeTTL;
24
+ function timestampFromDate(date) {
25
+ return timestampFromUnixMillis(date.getTime());
26
+ }
27
+ exports.timestampFromDate = timestampFromDate;
28
+ function timestampFromUnixMillis(msSinceEpoch) {
29
+ return {
30
+ seconds: Math.floor(msSinceEpoch / 1000),
31
+ nanoseconds: Math.floor((msSinceEpoch % 1000) * 1000000),
32
+ };
33
+ }
34
+ exports.timestampFromUnixMillis = timestampFromUnixMillis;
35
+ function nowTimestamp() {
36
+ // TODO: is there a way to use the server time rather than Date.now()?
37
+ return timestampFromUnixMillis(Date.now());
38
+ }
39
+ exports.nowTimestamp = nowTimestamp;
40
+ //# sourceMappingURL=Timestamps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Timestamps.js","sourceRoot":"","sources":["../src/Timestamps.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AAExB;;;GAGG;AACU,QAAA,eAAe,GAAG,OAAC,CAAC,MAAM,CAAC;IACtC,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE;IACpC,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE;CAC5C,CAAC,CAAC;AAGH,8EAA8E;AAC9E,SAAS;AAET,SAAgB,iBAAiB,CAAC,SAAwB;IACxD,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,GAAG,GAAG,SAAS,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC;AACzE,CAAC;AAFD,8CAEC;AAED,SAAgB,OAAO,CAAC,WAAW,GAAG,EAAE;IACtC,sEAAsE;IACtE,OAAO,uBAAuB,CAC5B,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAC/C,CAAC;AACJ,CAAC;AALD,0BAKC;AAED,SAAgB,iBAAiB,CAAC,IAAU;IAC1C,OAAO,uBAAuB,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;AACjD,CAAC;AAFD,8CAEC;AAED,SAAgB,uBAAuB,CAAC,YAAoB;IAC1D,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;QACxC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC;KACzD,CAAC;AACJ,CAAC;AALD,0DAKC;AAED,SAAgB,YAAY;IAC1B,sEAAsE;IACtE,OAAO,uBAAuB,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;AAC7C,CAAC;AAHD,oCAGC"}
@@ -0,0 +1,3 @@
1
+ export { DocWrapper } from "./DocWrapper";
2
+ export { FireTenderDoc } from "./FireTenderDoc";
3
+ export * from "./Timestamps";
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.FireTenderDoc = exports.DocWrapper = void 0;
18
+ var DocWrapper_1 = require("./DocWrapper");
19
+ Object.defineProperty(exports, "DocWrapper", { enumerable: true, get: function () { return DocWrapper_1.DocWrapper; } });
20
+ var FireTenderDoc_1 = require("./FireTenderDoc");
21
+ Object.defineProperty(exports, "FireTenderDoc", { enumerable: true, get: function () { return FireTenderDoc_1.FireTenderDoc; } });
22
+ __exportStar(require("./Timestamps"), exports);
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,+CAA6B"}
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+ export declare function watchArrayForChanges<ArrayElementType, FieldSchemaType extends z.ZodTypeAny>(arrayPath: string[], array: ArrayElementType[], fieldSchema: FieldSchemaType, field: z.infer<FieldSchemaType>, onChange: (path: string[], newValue: any) => void): z.infer<FieldSchemaType>;
3
+ export declare function watchFieldForChanges<FieldSchemaType extends z.ZodTypeAny>(fieldPath: string[], fieldSchema: FieldSchemaType, field: z.infer<FieldSchemaType>, onChange: (path: string[], newValue: any) => void): z.infer<FieldSchemaType>;
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.watchFieldForChanges = exports.watchArrayForChanges = void 0;
4
+ const firestore_1 = require("firebase/firestore");
5
+ const zod_1 = require("zod");
6
+ function assertKeyIsString(key) {
7
+ if (typeof key !== "string") {
8
+ throw TypeError("Property access using symbols is not supported.");
9
+ }
10
+ }
11
+ function unwrapSchema(schema) {
12
+ if (schema instanceof zod_1.z.ZodOptional || schema instanceof zod_1.z.ZodNullable) {
13
+ return unwrapSchema(schema.unwrap());
14
+ }
15
+ if (schema instanceof zod_1.z.ZodDefault) {
16
+ return unwrapSchema(schema.removeDefault());
17
+ }
18
+ if (schema instanceof zod_1.z.ZodEffects) {
19
+ return unwrapSchema(schema.innerType());
20
+ }
21
+ return schema;
22
+ }
23
+ function getPropertySchema(parentSchema, propertyKey) {
24
+ const schema = unwrapSchema(parentSchema);
25
+ if (schema instanceof zod_1.z.ZodRecord) {
26
+ return schema.valueSchema;
27
+ }
28
+ if (schema instanceof zod_1.z.ZodArray) {
29
+ return schema.element;
30
+ }
31
+ if (schema instanceof zod_1.z.ZodObject) {
32
+ return schema.shape[propertyKey];
33
+ }
34
+ throw TypeError(`Unsupported schema type for property "${propertyKey}": ${schema.constructor.name}`);
35
+ }
36
+ function watchArrayForChanges(arrayPath, array, fieldSchema, field, onChange) {
37
+ return new Proxy(field, {
38
+ get(target, propertyKey) {
39
+ assertKeyIsString(propertyKey);
40
+ const property = target[propertyKey];
41
+ if (property instanceof Function) {
42
+ const result = (...args) => property.apply(field, args);
43
+ // TODO (easy): have a list of functions that don't trigger onChange.
44
+ // TODO (harder): also handle at, foreach, etc. methods to chain proxies
45
+ // down from them. But life's too short for that.
46
+ onChange(arrayPath, array);
47
+ return result;
48
+ }
49
+ if (property instanceof Object) {
50
+ return watchArrayForChanges(arrayPath, array, getPropertySchema(fieldSchema, propertyKey), property, onChange);
51
+ }
52
+ return property;
53
+ },
54
+ set(target, propertyKey, value) {
55
+ assertKeyIsString(propertyKey);
56
+ const propertySchema = getPropertySchema(fieldSchema, propertyKey);
57
+ const parsedValue = propertySchema.parse(value);
58
+ const result = Reflect.set(target, propertyKey, parsedValue);
59
+ onChange(arrayPath, array);
60
+ return result;
61
+ },
62
+ deleteProperty(target, propertyKey) {
63
+ assertKeyIsString(propertyKey);
64
+ // Calling Reflect.deleteProperty on an array item sets it to undefined,
65
+ // which causes Firestore updates to fail unless ignoreUndefinedProperties
66
+ // is set, and which is generally not what we want. Hence splice.
67
+ const removedValues = array.splice(Number(propertyKey), 1);
68
+ if (removedValues.length !== 1) {
69
+ throw RangeError(`Failed to delete array item with index ${propertyKey}. Out of bounds?`);
70
+ }
71
+ if (target === array) {
72
+ onChange(arrayPath, (0, firestore_1.arrayRemove)(removedValues[0]));
73
+ }
74
+ else {
75
+ onChange(arrayPath, array);
76
+ }
77
+ return true;
78
+ },
79
+ });
80
+ }
81
+ exports.watchArrayForChanges = watchArrayForChanges;
82
+ function watchFieldForChanges(fieldPath, fieldSchema, field, onChange) {
83
+ return new Proxy(field, {
84
+ get(target, propertyKey) {
85
+ assertKeyIsString(propertyKey);
86
+ const property = target[propertyKey];
87
+ if (property instanceof Function) {
88
+ return (...args) => property.apply(field, args);
89
+ }
90
+ if (property instanceof Array) {
91
+ return watchArrayForChanges([...fieldPath, propertyKey], property, getPropertySchema(fieldSchema, propertyKey), property, onChange);
92
+ }
93
+ if (property instanceof Object) {
94
+ return watchFieldForChanges([...fieldPath, propertyKey], getPropertySchema(fieldSchema, propertyKey), property, onChange);
95
+ }
96
+ return property;
97
+ },
98
+ set(target, propertyKey, value) {
99
+ assertKeyIsString(propertyKey);
100
+ const propertySchema = getPropertySchema(fieldSchema, propertyKey);
101
+ const parsedValue = propertySchema.parse(value);
102
+ onChange([...fieldPath, propertyKey], parsedValue);
103
+ return Reflect.set(target, propertyKey, parsedValue);
104
+ },
105
+ deleteProperty(target, propertyKey) {
106
+ assertKeyIsString(propertyKey);
107
+ onChange([...fieldPath, propertyKey], (0, firestore_1.deleteField)());
108
+ return Reflect.deleteProperty(target, propertyKey);
109
+ },
110
+ });
111
+ }
112
+ exports.watchFieldForChanges = watchFieldForChanges;
113
+ //# sourceMappingURL=proxies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxies.js","sourceRoot":"","sources":["../src/proxies.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "firetender",
3
+ "displayName": "FireTender",
4
+ "description": "Typescript wrapper for Firestore documents",
5
+ "version": "0.1.0",
6
+ "author": "Jake Hartman",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/jakes-space/firetender",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/jakes-space/firetender.git"
12
+ },
13
+ "keywords": [
14
+ "firestore",
15
+ "typescript",
16
+ "schema"
17
+ ],
18
+ "types": "./dist/index.d.ts",
19
+ "main": "./dist/index.js",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "/dist"
28
+ ],
29
+ "scripts": {
30
+ "lint": "eslint",
31
+ "test": "jest"
32
+ },
33
+ "dependencies": {
34
+ "firebase": "^9.13.0",
35
+ "zod": "^3.19.1"
36
+ },
37
+ "devDependencies": {
38
+ "@firebase/rules-unit-testing": "^2.0.5",
39
+ "@rushstack/eslint-patch": "^1.2.0",
40
+ "@types/jest": "^29.2.0",
41
+ "@typescript-eslint/eslint-plugin": "^5.41.0",
42
+ "@typescript-eslint/parser": "^5.41.0",
43
+ "eslint": "^8.26.0",
44
+ "eslint-config-prettier": "^8.5.0",
45
+ "eslint-import-resolver-typescript": "^3.5.2",
46
+ "eslint-plugin-import": "^2.26.0",
47
+ "jest": "^29.2.2",
48
+ "prettier": "^2.7.1",
49
+ "ts-jest": "^29.0.3",
50
+ "tslib": "^2.4.1",
51
+ "typescript": "^4.8.4"
52
+ }
53
+ }