firetender 0.10.2 → 0.10.3

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.
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FiretenderDoc = void 0;
4
4
  const errors_1 = require("./errors");
5
5
  const firestore_deps_1 = require("./firestore-deps");
6
- const proxies_1 = require("./proxies");
6
+ const proxy_1 = require("./proxy");
7
7
  const ts_helpers_1 = require("./ts-helpers");
8
8
  /**
9
9
  * A local representation of a Firestore document.
@@ -246,7 +246,7 @@ class FiretenderDoc {
246
246
  // TODO #23: Consider being able to update a doc without loading it.
247
247
  throw new errors_1.FiretenderUsageError("load() must be called before updating the document.");
248
248
  }
249
- this.dataProxy = (0, proxies_1.watchFieldForChanges)([], this.schema, this.data, this.addToUpdateList.bind(this));
249
+ this.dataProxy = (0, proxy_1.watchForChanges)([], this.schema, this.data, this.addToUpdateList.bind(this));
250
250
  }
251
251
  return this.dataProxy;
252
252
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FiretenderDoc.js","sourceRoot":"","sources":["../src/FiretenderDoc.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FiretenderDoc.js","sourceRoot":"","sources":["../src/FiretenderDoc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Wraps an object based on a Zod schema in a proxy that watches for changes.
4
+ *
5
+ * Nested fields are monitored by creating a chain of proxies. Consider this
6
+ * case:
7
+ *
8
+ * testDoc.w.record1.record2.someStringValue = "foo";
9
+ *
10
+ * There is a top-level proxy (call it proxy 0) for the ".w" accessor. Getting
11
+ * ".record1" from it returns a child proxy (1) wrapping that field. Getting
12
+ * ".record2" from that returns another child proxy (2) wrapping that subfield.
13
+ * Finally, setting ".someStringValue" uses the setter of proxy 2 to set the new
14
+ * value locally and register an update to be sent to Firestore.
15
+ *
16
+ * If the wrapped field or one of its ancestors is an array, this won't work.
17
+ * Firestore supports limited modification of arrays; see the 2018 blog entry at
18
+ * https://firebase.blog/posts/2018/08/better-arrays-in-cloud-firestore for
19
+ * details and some history. The tl;dr is that we can't update array entries,
20
+ * and arrayUnion() and arrayRemove() treat the array as a set. So we have to
21
+ * update the entire top-level array. Child proxies maintain a link to the
22
+ * top-level array ancestor and trigger a rewrite of the whole thing if there
23
+ * are any changes to the contents.
24
+ *
25
+ * @param updatePath the Firestore path to be updated, as an array of strings.
26
+ * @param fieldSchema schema of the object being wrapped.
27
+ * @param field reference to the field in the local document data.
28
+ * @param addToUpdateList callback to register updates to updatePath.
29
+ * @param arrayAncestor a reference to the top-level array, if any.
30
+ */
31
+ export declare function watchForChanges<FieldSchemaType extends z.ZodTypeAny, ArrayElementType>(updatePath: string[], fieldSchema: FieldSchemaType, field: z.infer<FieldSchemaType>, addToUpdateList: (path: string[], newValue: any) => void, arrayAncestor?: ArrayElementType[] | undefined): z.infer<FieldSchemaType>;
package/dist/proxy.js ADDED
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.watchForChanges = void 0;
4
+ const zod_1 = require("zod");
5
+ const firestore_deps_1 = require("./firestore-deps");
6
+ /**
7
+ * Getting this symbol from one of our proxies returns the proxy's target.
8
+ */
9
+ const PROXY_TARGET_SYMBOL = Symbol("proxy_target");
10
+ /**
11
+ * Wraps an object based on a Zod schema in a proxy that watches for changes.
12
+ *
13
+ * Nested fields are monitored by creating a chain of proxies. Consider this
14
+ * case:
15
+ *
16
+ * testDoc.w.record1.record2.someStringValue = "foo";
17
+ *
18
+ * There is a top-level proxy (call it proxy 0) for the ".w" accessor. Getting
19
+ * ".record1" from it returns a child proxy (1) wrapping that field. Getting
20
+ * ".record2" from that returns another child proxy (2) wrapping that subfield.
21
+ * Finally, setting ".someStringValue" uses the setter of proxy 2 to set the new
22
+ * value locally and register an update to be sent to Firestore.
23
+ *
24
+ * If the wrapped field or one of its ancestors is an array, this won't work.
25
+ * Firestore supports limited modification of arrays; see the 2018 blog entry at
26
+ * https://firebase.blog/posts/2018/08/better-arrays-in-cloud-firestore for
27
+ * details and some history. The tl;dr is that we can't update array entries,
28
+ * and arrayUnion() and arrayRemove() treat the array as a set. So we have to
29
+ * update the entire top-level array. Child proxies maintain a link to the
30
+ * top-level array ancestor and trigger a rewrite of the whole thing if there
31
+ * are any changes to the contents.
32
+ *
33
+ * @param updatePath the Firestore path to be updated, as an array of strings.
34
+ * @param fieldSchema schema of the object being wrapped.
35
+ * @param field reference to the field in the local document data.
36
+ * @param addToUpdateList callback to register updates to updatePath.
37
+ * @param arrayAncestor a reference to the top-level array, if any.
38
+ */
39
+ function watchForChanges(updatePath, fieldSchema, field, addToUpdateList, arrayAncestor = undefined) {
40
+ return new Proxy(field, {
41
+ get(target, propertyKey) {
42
+ const property = target[propertyKey];
43
+ if (property instanceof Function) {
44
+ const result = (...args) => property.apply(field, args);
45
+ if (arrayAncestor) {
46
+ // All methods of an array or its children are presumed to make
47
+ // modifications, thus triggering an update of the full array.
48
+ //
49
+ // TODO: #11 Only mark updates for mutating array methods.
50
+ //
51
+ // Methods of all other objects are ignored. This is safe because the
52
+ // only other allowed objects are Records, which do not have mutating
53
+ // methods.
54
+ addToUpdateList(updatePath, arrayAncestor);
55
+ }
56
+ return result;
57
+ }
58
+ if (typeof propertyKey === "symbol") {
59
+ // PROXY_TARGET_SYMBOL unwraps this proxy.
60
+ if (propertyKey === PROXY_TARGET_SYMBOL) {
61
+ return target;
62
+ }
63
+ // Allow all other symbols to pass through.
64
+ return property;
65
+ }
66
+ if (property instanceof Object) {
67
+ // Child objects need to be wrapped with another watchForChanges() call.
68
+ let nextPath;
69
+ let nextArrayAncestor;
70
+ if (arrayAncestor) {
71
+ // Array ancestors should continue pointing to the top-level array.
72
+ nextPath = updatePath;
73
+ nextArrayAncestor = arrayAncestor;
74
+ }
75
+ else {
76
+ // Otherwise the next proxy should point to this child. If it's an
77
+ // array, establish it as a top-level array.
78
+ nextPath = [...updatePath, propertyKey];
79
+ if (property instanceof Array) {
80
+ nextArrayAncestor = property;
81
+ }
82
+ }
83
+ return watchForChanges(nextPath, getPropertySchema(field, fieldSchema, propertyKey), property, addToUpdateList, nextArrayAncestor);
84
+ }
85
+ // Otherwise we must be getting a primitive. No need to wrap it.
86
+ return property;
87
+ },
88
+ set(target, propertyKey, value) {
89
+ if (typeof propertyKey === "symbol") {
90
+ // Allow symbols to pass through.
91
+ return Reflect.set(target, propertyKey, value);
92
+ }
93
+ let processedValue = value;
94
+ // If the new value is an object wrapped in a Firetender proxy, which can
95
+ // commonly happen when referencing it inside a mutator function passed to
96
+ // FiretenderDoc.prototype.update(), unwrap it.
97
+ if (value instanceof Object && value[PROXY_TARGET_SYMBOL]) {
98
+ processedValue = value[PROXY_TARGET_SYMBOL];
99
+ }
100
+ // A property of this object is being set to a new value. Parse the new
101
+ // value with the appropriate schema, set it in the local data, and mark
102
+ // the property (if we aren't inside an array) or the entire top-level
103
+ // array (if we are) as needing to be written. If the new value is
104
+ // undefined, delete the property.
105
+ const propertySchema = getPropertySchema(field, fieldSchema, propertyKey);
106
+ processedValue = propertySchema.parse(processedValue);
107
+ let result;
108
+ if (processedValue === undefined) {
109
+ processedValue = (0, firestore_deps_1.deleteField)();
110
+ result = Reflect.deleteProperty(target, propertyKey);
111
+ }
112
+ else {
113
+ processedValue = pruneUndefinedFields(processedValue);
114
+ result = Reflect.set(target, propertyKey, processedValue);
115
+ }
116
+ if (arrayAncestor) {
117
+ addToUpdateList(updatePath, arrayAncestor);
118
+ }
119
+ else {
120
+ addToUpdateList([...updatePath, propertyKey], processedValue);
121
+ }
122
+ return result;
123
+ },
124
+ deleteProperty(target, propertyKey) {
125
+ if (typeof propertyKey === "symbol") {
126
+ // Allow symbols to pass through.
127
+ return Reflect.deleteProperty(target, propertyKey);
128
+ }
129
+ let result = true;
130
+ if (target instanceof Array) {
131
+ // Calling Reflect.deleteProperty on an array item sets it to undefined,
132
+ // which causes Firestore writes to fail if ignoreUndefinedProperties is
133
+ // not set, and which is generally not what we want. Hence splice.
134
+ if (!propertyKey.match(/^\d+$/)) {
135
+ throw TypeError(`Failed to delete an invalid array index: "${propertyKey}".`);
136
+ }
137
+ const index = Number(propertyKey);
138
+ target.splice(index, 1);
139
+ // The delete operator pretty much always returns true, so do the same.
140
+ }
141
+ else {
142
+ result = Reflect.deleteProperty(target, propertyKey);
143
+ }
144
+ if (arrayAncestor) {
145
+ // Firestore's arrayRemove() deletes all matching entries, which is not
146
+ // desired. So we have to rewrite the full array.
147
+ addToUpdateList(updatePath, arrayAncestor);
148
+ }
149
+ else {
150
+ addToUpdateList([...updatePath, propertyKey], (0, firestore_deps_1.deleteField)());
151
+ }
152
+ return result;
153
+ },
154
+ });
155
+ }
156
+ exports.watchForChanges = watchForChanges;
157
+ /**
158
+ * Given a Zod schema representing a collection, returns the sub-schema of the
159
+ * specified property.
160
+ */
161
+ function getPropertySchema(parent, parentSchema, propertyKey) {
162
+ let schema = parentSchema;
163
+ // eslint-disable-next-line no-constant-condition
164
+ while (true) {
165
+ switch (schema._def.typeName) {
166
+ // If the schema object is wrapped (e.g., by being optional or having a
167
+ // default), unwrap it until we get to the underlying collection type.
168
+ case zod_1.z.ZodFirstPartyTypeKind.ZodOptional:
169
+ case zod_1.z.ZodFirstPartyTypeKind.ZodNullable:
170
+ schema = schema.unwrap();
171
+ continue;
172
+ case zod_1.z.ZodFirstPartyTypeKind.ZodDefault:
173
+ schema = schema.removeDefault();
174
+ continue;
175
+ case zod_1.z.ZodFirstPartyTypeKind.ZodEffects:
176
+ schema = schema.innerType();
177
+ continue;
178
+ // Return the sub-schemas of supported collection types.
179
+ case zod_1.z.ZodFirstPartyTypeKind.ZodRecord:
180
+ if (schema.keySchema._def.typeName !== zod_1.z.ZodFirstPartyTypeKind.ZodString) {
181
+ throw TypeError(`The ZodRecord for property ${propertyKey} has keys of type ${schema.keySchema._def.typeName}. Only strings are supported.`);
182
+ }
183
+ return schema.valueSchema;
184
+ case zod_1.z.ZodFirstPartyTypeKind.ZodArray:
185
+ return schema.element;
186
+ case zod_1.z.ZodFirstPartyTypeKind.ZodObject:
187
+ return schema.shape[propertyKey];
188
+ case zod_1.z.ZodFirstPartyTypeKind.ZodDiscriminatedUnion:
189
+ return schema.optionsMap.get(parent[schema.discriminator]);
190
+ // If the parent is of type ZodAny, so are its properties.
191
+ case zod_1.z.ZodFirstPartyTypeKind.ZodAny:
192
+ return zod_1.z.any();
193
+ default:
194
+ throw TypeError(`Unsupported schema type for property "${propertyKey}": ${schema._def.typeName}`);
195
+ }
196
+ }
197
+ }
198
+ /**
199
+ * Returns a deep copy of the given object, omitting any undefined fields.
200
+ *
201
+ * Note: Timestamps and server timestamps pass through unmodified, but all other
202
+ * objects will be stripped of their methods.
203
+ */
204
+ function pruneUndefinedFields(obj) {
205
+ if (typeof obj !== "object" ||
206
+ obj === null ||
207
+ obj instanceof firestore_deps_1.Timestamp ||
208
+ (0, firestore_deps_1.isServerTimestamp)(obj)) {
209
+ return obj;
210
+ }
211
+ if (obj instanceof Array) {
212
+ return obj
213
+ .filter((v) => v !== undefined)
214
+ .map((v) => pruneUndefinedFields(v));
215
+ }
216
+ return Object.fromEntries(Object.entries(obj)
217
+ .filter(([_k, v]) => v !== undefined)
218
+ .map(([k, v]) => [k, pruneUndefinedFields(v)]));
219
+ }
220
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
@@ -11,4 +11,3 @@ export type MakeFieldsWithDefaultsOptional<T> = T extends Array<infer ArrKey> ?
11
11
  readonly [ObjKey in keyof T]: DeepReadonly<T[ObjKey]>;
12
12
  } : T;
13
13
  export declare function assertIsDefined<T>(value: T): asserts value is NonNullable<T>;
14
- export declare function assertKeyIsString(key: any): asserts key is string;
@@ -3,17 +3,11 @@
3
3
  * Typescript-related helper functions and types.
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.assertKeyIsString = exports.assertIsDefined = void 0;
6
+ exports.assertIsDefined = void 0;
7
7
  function assertIsDefined(value) {
8
8
  if (value === undefined || value === null) {
9
9
  throw new TypeError(`${value} is not defined`);
10
10
  }
11
11
  }
12
12
  exports.assertIsDefined = assertIsDefined;
13
- function assertKeyIsString(key) {
14
- if (typeof key !== "string") {
15
- throw TypeError("Property access using symbols is not supported.");
16
- }
17
- }
18
- exports.assertKeyIsString = assertKeyIsString;
19
13
  //# sourceMappingURL=ts-helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ts-helpers.js","sourceRoot":"","sources":["../src/ts-helpers.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AA0BH,SAAgB,eAAe,CAAI,KAAQ;IACzC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE;QACzC,MAAM,IAAI,SAAS,CAAC,GAAG,KAAK,iBAAiB,CAAC,CAAC;KAChD;AACH,CAAC;AAJD,0CAIC;AAED,SAAgB,iBAAiB,CAAC,GAAQ;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;QAC3B,MAAM,SAAS,CAAC,iDAAiD,CAAC,CAAC;KACpE;AACH,CAAC;AAJD,8CAIC"}
1
+ {"version":3,"file":"ts-helpers.js","sourceRoot":"","sources":["../src/ts-helpers.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AA0BH,SAAgB,eAAe,CAAI,KAAQ;IACzC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE;QACzC,MAAM,IAAI,SAAS,CAAC,GAAG,KAAK,iBAAiB,CAAC,CAAC;KAChD;AACH,CAAC;AAJD,0CAIC"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "firetender",
3
3
  "displayName": "Firetender",
4
4
  "description": "Typescript wrapper for Firestore documents",
5
- "version": "0.10.2",
5
+ "version": "0.10.3",
6
6
  "author": "Jake Hartman",
7
7
  "license": "MIT",
8
8
  "homepage": "https://github.com/jakes-space/firetender",
@@ -17,7 +17,7 @@ import {
17
17
  setDoc,
18
18
  updateDoc,
19
19
  } from "./firestore-deps";
20
- import { watchFieldForChanges } from "./proxies";
20
+ import { watchForChanges } from "./proxy";
21
21
  import { assertIsDefined, DeepReadonly } from "./ts-helpers";
22
22
 
23
23
  /**
@@ -358,7 +358,7 @@ export class FiretenderDoc<SchemaType extends z.SomeZodObject> {
358
358
  "load() must be called before updating the document."
359
359
  );
360
360
  }
361
- this.dataProxy = watchFieldForChanges(
361
+ this.dataProxy = watchForChanges(
362
362
  [],
363
363
  this.schema,
364
364
  this.data,
package/src/proxy.ts ADDED
@@ -0,0 +1,248 @@
1
+ import { z } from "zod";
2
+
3
+ import { deleteField, isServerTimestamp, Timestamp } from "./firestore-deps";
4
+
5
+ /**
6
+ * Getting this symbol from one of our proxies returns the proxy's target.
7
+ */
8
+ const PROXY_TARGET_SYMBOL = Symbol("proxy_target");
9
+
10
+ /**
11
+ * Wraps an object based on a Zod schema in a proxy that watches for changes.
12
+ *
13
+ * Nested fields are monitored by creating a chain of proxies. Consider this
14
+ * case:
15
+ *
16
+ * testDoc.w.record1.record2.someStringValue = "foo";
17
+ *
18
+ * There is a top-level proxy (call it proxy 0) for the ".w" accessor. Getting
19
+ * ".record1" from it returns a child proxy (1) wrapping that field. Getting
20
+ * ".record2" from that returns another child proxy (2) wrapping that subfield.
21
+ * Finally, setting ".someStringValue" uses the setter of proxy 2 to set the new
22
+ * value locally and register an update to be sent to Firestore.
23
+ *
24
+ * If the wrapped field or one of its ancestors is an array, this won't work.
25
+ * Firestore supports limited modification of arrays; see the 2018 blog entry at
26
+ * https://firebase.blog/posts/2018/08/better-arrays-in-cloud-firestore for
27
+ * details and some history. The tl;dr is that we can't update array entries,
28
+ * and arrayUnion() and arrayRemove() treat the array as a set. So we have to
29
+ * update the entire top-level array. Child proxies maintain a link to the
30
+ * top-level array ancestor and trigger a rewrite of the whole thing if there
31
+ * are any changes to the contents.
32
+ *
33
+ * @param updatePath the Firestore path to be updated, as an array of strings.
34
+ * @param fieldSchema schema of the object being wrapped.
35
+ * @param field reference to the field in the local document data.
36
+ * @param addToUpdateList callback to register updates to updatePath.
37
+ * @param arrayAncestor a reference to the top-level array, if any.
38
+ */
39
+ export function watchForChanges<
40
+ FieldSchemaType extends z.ZodTypeAny,
41
+ ArrayElementType
42
+ >(
43
+ updatePath: string[],
44
+ fieldSchema: FieldSchemaType,
45
+ field: z.infer<FieldSchemaType>,
46
+ addToUpdateList: (path: string[], newValue: any) => void,
47
+ arrayAncestor: ArrayElementType[] | undefined = undefined
48
+ ): z.infer<FieldSchemaType> {
49
+ return new Proxy(field, {
50
+ get(target, propertyKey) {
51
+ const property = target[propertyKey];
52
+ if (property instanceof Function) {
53
+ const result = (...args: any[]) => property.apply(field, args);
54
+ if (arrayAncestor) {
55
+ // All methods of an array or its children are presumed to make
56
+ // modifications, thus triggering an update of the full array.
57
+ //
58
+ // TODO: #11 Only mark updates for mutating array methods.
59
+ //
60
+ // Methods of all other objects are ignored. This is safe because the
61
+ // only other allowed objects are Records, which do not have mutating
62
+ // methods.
63
+ addToUpdateList(updatePath, arrayAncestor);
64
+ }
65
+ return result;
66
+ }
67
+ if (typeof propertyKey === "symbol") {
68
+ // PROXY_TARGET_SYMBOL unwraps this proxy.
69
+ if (propertyKey === PROXY_TARGET_SYMBOL) {
70
+ return target;
71
+ }
72
+ // Allow all other symbols to pass through.
73
+ return property;
74
+ }
75
+ if (property instanceof Object) {
76
+ // Child objects need to be wrapped with another watchForChanges() call.
77
+ let nextPath: string[];
78
+ let nextArrayAncestor: any[] | undefined;
79
+ if (arrayAncestor) {
80
+ // Array ancestors should continue pointing to the top-level array.
81
+ nextPath = updatePath;
82
+ nextArrayAncestor = arrayAncestor;
83
+ } else {
84
+ // Otherwise the next proxy should point to this child. If it's an
85
+ // array, establish it as a top-level array.
86
+ nextPath = [...updatePath, propertyKey];
87
+ if (property instanceof Array) {
88
+ nextArrayAncestor = property;
89
+ }
90
+ }
91
+ return watchForChanges(
92
+ nextPath,
93
+ getPropertySchema(field, fieldSchema, propertyKey),
94
+ property,
95
+ addToUpdateList,
96
+ nextArrayAncestor
97
+ );
98
+ }
99
+ // Otherwise we must be getting a primitive. No need to wrap it.
100
+ return property;
101
+ },
102
+ set(target, propertyKey, value) {
103
+ if (typeof propertyKey === "symbol") {
104
+ // Allow symbols to pass through.
105
+ return Reflect.set(target, propertyKey, value);
106
+ }
107
+ let processedValue = value;
108
+ // If the new value is an object wrapped in a Firetender proxy, which can
109
+ // commonly happen when referencing it inside a mutator function passed to
110
+ // FiretenderDoc.prototype.update(), unwrap it.
111
+ if (value instanceof Object && value[PROXY_TARGET_SYMBOL]) {
112
+ processedValue = value[PROXY_TARGET_SYMBOL];
113
+ }
114
+ // A property of this object is being set to a new value. Parse the new
115
+ // value with the appropriate schema, set it in the local data, and mark
116
+ // the property (if we aren't inside an array) or the entire top-level
117
+ // array (if we are) as needing to be written. If the new value is
118
+ // undefined, delete the property.
119
+ const propertySchema = getPropertySchema(field, fieldSchema, propertyKey);
120
+ processedValue = propertySchema.parse(processedValue);
121
+ let result: boolean;
122
+ if (processedValue === undefined) {
123
+ processedValue = deleteField();
124
+ result = Reflect.deleteProperty(target, propertyKey);
125
+ } else {
126
+ processedValue = pruneUndefinedFields(processedValue);
127
+ result = Reflect.set(target, propertyKey, processedValue);
128
+ }
129
+ if (arrayAncestor) {
130
+ addToUpdateList(updatePath, arrayAncestor);
131
+ } else {
132
+ addToUpdateList([...updatePath, propertyKey], processedValue);
133
+ }
134
+ return result;
135
+ },
136
+ deleteProperty(target, propertyKey) {
137
+ if (typeof propertyKey === "symbol") {
138
+ // Allow symbols to pass through.
139
+ return Reflect.deleteProperty(target, propertyKey);
140
+ }
141
+ let result = true;
142
+ if ((target as any) instanceof Array) {
143
+ // Calling Reflect.deleteProperty on an array item sets it to undefined,
144
+ // which causes Firestore writes to fail if ignoreUndefinedProperties is
145
+ // not set, and which is generally not what we want. Hence splice.
146
+ if (!propertyKey.match(/^\d+$/)) {
147
+ throw TypeError(
148
+ `Failed to delete an invalid array index: "${propertyKey}".`
149
+ );
150
+ }
151
+ const index = Number(propertyKey);
152
+ target.splice(index, 1);
153
+ // The delete operator pretty much always returns true, so do the same.
154
+ } else {
155
+ result = Reflect.deleteProperty(target, propertyKey);
156
+ }
157
+ if (arrayAncestor) {
158
+ // Firestore's arrayRemove() deletes all matching entries, which is not
159
+ // desired. So we have to rewrite the full array.
160
+ addToUpdateList(updatePath, arrayAncestor);
161
+ } else {
162
+ addToUpdateList([...updatePath, propertyKey], deleteField());
163
+ }
164
+ return result;
165
+ },
166
+ });
167
+ }
168
+
169
+ /**
170
+ * Given a Zod schema representing a collection, returns the sub-schema of the
171
+ * specified property.
172
+ */
173
+ function getPropertySchema(
174
+ parent: any,
175
+ parentSchema: z.ZodTypeAny,
176
+ propertyKey: string
177
+ ): z.ZodTypeAny {
178
+ let schema: any = parentSchema;
179
+ // eslint-disable-next-line no-constant-condition
180
+ while (true) {
181
+ switch (schema._def.typeName) {
182
+ // If the schema object is wrapped (e.g., by being optional or having a
183
+ // default), unwrap it until we get to the underlying collection type.
184
+ case z.ZodFirstPartyTypeKind.ZodOptional:
185
+ case z.ZodFirstPartyTypeKind.ZodNullable:
186
+ schema = schema.unwrap();
187
+ continue;
188
+ case z.ZodFirstPartyTypeKind.ZodDefault:
189
+ schema = schema.removeDefault();
190
+ continue;
191
+ case z.ZodFirstPartyTypeKind.ZodEffects:
192
+ schema = schema.innerType();
193
+ continue;
194
+ // Return the sub-schemas of supported collection types.
195
+ case z.ZodFirstPartyTypeKind.ZodRecord:
196
+ if (
197
+ schema.keySchema._def.typeName !== z.ZodFirstPartyTypeKind.ZodString
198
+ ) {
199
+ throw TypeError(
200
+ `The ZodRecord for property ${propertyKey} has keys of type ${schema.keySchema._def.typeName}. Only strings are supported.`
201
+ );
202
+ }
203
+ return schema.valueSchema;
204
+ case z.ZodFirstPartyTypeKind.ZodArray:
205
+ return schema.element;
206
+ case z.ZodFirstPartyTypeKind.ZodObject:
207
+ return schema.shape[propertyKey];
208
+ case z.ZodFirstPartyTypeKind.ZodDiscriminatedUnion:
209
+ return (schema as any).optionsMap.get(
210
+ parent[(schema as any).discriminator]
211
+ );
212
+ // If the parent is of type ZodAny, so are its properties.
213
+ case z.ZodFirstPartyTypeKind.ZodAny:
214
+ return z.any();
215
+ default:
216
+ throw TypeError(
217
+ `Unsupported schema type for property "${propertyKey}": ${schema._def.typeName}`
218
+ );
219
+ }
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Returns a deep copy of the given object, omitting any undefined fields.
225
+ *
226
+ * Note: Timestamps and server timestamps pass through unmodified, but all other
227
+ * objects will be stripped of their methods.
228
+ */
229
+ function pruneUndefinedFields<T>(obj: T): T {
230
+ if (
231
+ typeof obj !== "object" ||
232
+ obj === null ||
233
+ obj instanceof Timestamp ||
234
+ isServerTimestamp(obj)
235
+ ) {
236
+ return obj;
237
+ }
238
+ if (obj instanceof Array) {
239
+ return obj
240
+ .filter((v) => v !== undefined)
241
+ .map((v) => pruneUndefinedFields(v)) as T;
242
+ }
243
+ return Object.fromEntries(
244
+ Object.entries(obj as Record<string | number | symbol, unknown>)
245
+ .filter(([_k, v]) => v !== undefined)
246
+ .map(([k, v]) => [k, pruneUndefinedFields(v)])
247
+ ) as T;
248
+ }
package/src/ts-helpers.ts CHANGED
@@ -31,9 +31,3 @@ export function assertIsDefined<T>(value: T): asserts value is NonNullable<T> {
31
31
  throw new TypeError(`${value} is not defined`);
32
32
  }
33
33
  }
34
-
35
- export function assertKeyIsString(key: any): asserts key is string {
36
- if (typeof key !== "string") {
37
- throw TypeError("Property access using symbols is not supported.");
38
- }
39
- }
package/dist/proxies.d.ts DELETED
@@ -1,62 +0,0 @@
1
- import { z } from "zod";
2
- /**
3
- * Wraps a top-level array, its elements, or its elements' subfields in a proxy
4
- * that watches for changes.
5
- *
6
- * Firestore supports limited modification of arrays; see the 2018 blog entry at
7
- * https://firebase.blog/posts/2018/08/better-arrays-in-cloud-firestore for
8
- * details and some history. The tl;dr is that we can't update array entries;
9
- * we can only append or remove them.
10
- *
11
- * For this reason, this proxy only works with top-level arrays: the wrapped
12
- * array cannot be inside a parent array, though it may be in a record or nested
13
- * records. Child proxies maintain a link to the top-level array and trigger a
14
- * rewrite of the whole thing if there are any changes to the contents. As a
15
- * consequence, the watchFieldForChanges proxy (below) can produce child
16
- * watchArrayForChanges proxies, but all children of watchArrayForChanges will
17
- * always use the watchArrayForChanges proxy to maintain a reference to the
18
- * top-level array.
19
- *
20
- * Appending an element to the top-level array could in theory be supported, but
21
- * Firestore's arrayUnion operator only appends entries that don't already
22
- * exist. That is not how Javascript arrays work, so to reduce logical overhead
23
- * we don't bother with it.
24
- *
25
- * Deletion of entries in the top-level array are handled using Firestore's
26
- * arrayRemove operator. The deleted element gets removed entirely, so the
27
- * resulting array remains dense. Note that this behavior differs from how
28
- * Javascript's delete operator normally works with arrays, but here we favor
29
- * Firestore's array semantics.
30
- *
31
- * @param arrayPath the dot-delimited path of the top-level array.
32
- * @param array a reference to the top-level array.
33
- * @param fieldSchema schema of the array element or a field nested within it.
34
- * @param field reference to the array element or one of its subfields.
35
- * @param addToUpdateList callback to register modifications to this array.
36
- */
37
- export declare function watchArrayForChanges<ArrayElementType, FieldSchemaType extends z.ZodTypeAny>(arrayPath: string[], array: ArrayElementType[], fieldSchema: FieldSchemaType, field: z.infer<FieldSchemaType>, addToUpdateList: (path: string[], newValue: any) => void): z.infer<FieldSchemaType>;
38
- /**
39
- * Wraps an object based on a Zod schema in a proxy that watches for changes.
40
- *
41
- * Nested fields are monitored by creating a chain of proxies. Consider this
42
- * case:
43
- *
44
- * testDoc.w.record1.record2.someStringValue = "foo";
45
- *
46
- * There is a top-level proxy (call it proxy 0) for the ".w" accessor. Getting
47
- * ".record1" from it returns a child proxy (1) wrapping that field. Getting
48
- * ".record2" from that returns another child proxy (2) wrapping that subfield.
49
- * Finally, setting ".someStringValue" uses the setter of proxy 2 to set the new
50
- * value locally and register an update to be sent to Firestore.
51
- *
52
- * The proxy returned by watchFieldForChanges is used for fields and subfields
53
- * when no parent is an array. If a field or subfield is an array, the proxy
54
- * returned by watchArrayForChanges is used for it and all of its children,
55
- * regardless of whether they are arrays.
56
- *
57
- * @param fieldPath the dot-delimited path of the object being wrapped.
58
- * @param fieldSchema schema of the object's field.
59
- * @param field reference to the field in the local document data.
60
- * @param addToUpdateList callback to register modifications to this field.
61
- */
62
- export declare function watchFieldForChanges<FieldSchemaType extends z.ZodTypeAny>(fieldPath: string[], fieldSchema: FieldSchemaType, field: z.infer<FieldSchemaType>, addToUpdateList: (path: string[], newValue: any) => void): z.infer<FieldSchemaType>;
package/dist/proxies.js DELETED
@@ -1,258 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.watchFieldForChanges = exports.watchArrayForChanges = void 0;
4
- const zod_1 = require("zod");
5
- const firestore_deps_1 = require("./firestore-deps");
6
- const ts_helpers_1 = require("./ts-helpers");
7
- /**
8
- * Given a Zod schema representing a collection, returns the sub-schema of the
9
- * specified property.
10
- */
11
- function getPropertySchema(parent, parentSchema, propertyKey) {
12
- let schema = parentSchema;
13
- // eslint-disable-next-line no-constant-condition
14
- while (true) {
15
- switch (schema._def.typeName) {
16
- // If the schema object is wrapped (e.g., by being optional or having a
17
- // default), unwrap it until we get to the underlying collection type.
18
- case zod_1.z.ZodFirstPartyTypeKind.ZodOptional:
19
- case zod_1.z.ZodFirstPartyTypeKind.ZodNullable:
20
- schema = schema.unwrap();
21
- continue;
22
- case zod_1.z.ZodFirstPartyTypeKind.ZodDefault:
23
- schema = schema.removeDefault();
24
- continue;
25
- case zod_1.z.ZodFirstPartyTypeKind.ZodEffects:
26
- schema = schema.innerType();
27
- continue;
28
- // Return the sub-schemas of supported collection types.
29
- case zod_1.z.ZodFirstPartyTypeKind.ZodRecord:
30
- if (schema.keySchema._def.typeName !== zod_1.z.ZodFirstPartyTypeKind.ZodString) {
31
- throw TypeError(`The ZodRecord for property ${propertyKey} has keys of type ${schema.keySchema._def.typeName}. Only strings are supported.`);
32
- }
33
- return schema.valueSchema;
34
- case zod_1.z.ZodFirstPartyTypeKind.ZodArray:
35
- return schema.element;
36
- case zod_1.z.ZodFirstPartyTypeKind.ZodObject:
37
- return schema.shape[propertyKey];
38
- case zod_1.z.ZodFirstPartyTypeKind.ZodDiscriminatedUnion:
39
- return schema.optionsMap.get(parent[schema.discriminator]);
40
- // If the parent is of type ZodAny, so are its properties.
41
- case zod_1.z.ZodFirstPartyTypeKind.ZodAny:
42
- return zod_1.z.any();
43
- default:
44
- throw TypeError(`Unsupported schema type for property "${propertyKey}": ${schema._def.typeName}`);
45
- }
46
- }
47
- }
48
- /**
49
- * Getting this symbol from one of our proxies returns the proxy's target.
50
- */
51
- const PROXY_TARGET_SYMBOL = Symbol("proxy_target");
52
- /**
53
- * Wraps a top-level array, its elements, or its elements' subfields in a proxy
54
- * that watches for changes.
55
- *
56
- * Firestore supports limited modification of arrays; see the 2018 blog entry at
57
- * https://firebase.blog/posts/2018/08/better-arrays-in-cloud-firestore for
58
- * details and some history. The tl;dr is that we can't update array entries;
59
- * we can only append or remove them.
60
- *
61
- * For this reason, this proxy only works with top-level arrays: the wrapped
62
- * array cannot be inside a parent array, though it may be in a record or nested
63
- * records. Child proxies maintain a link to the top-level array and trigger a
64
- * rewrite of the whole thing if there are any changes to the contents. As a
65
- * consequence, the watchFieldForChanges proxy (below) can produce child
66
- * watchArrayForChanges proxies, but all children of watchArrayForChanges will
67
- * always use the watchArrayForChanges proxy to maintain a reference to the
68
- * top-level array.
69
- *
70
- * Appending an element to the top-level array could in theory be supported, but
71
- * Firestore's arrayUnion operator only appends entries that don't already
72
- * exist. That is not how Javascript arrays work, so to reduce logical overhead
73
- * we don't bother with it.
74
- *
75
- * Deletion of entries in the top-level array are handled using Firestore's
76
- * arrayRemove operator. The deleted element gets removed entirely, so the
77
- * resulting array remains dense. Note that this behavior differs from how
78
- * Javascript's delete operator normally works with arrays, but here we favor
79
- * Firestore's array semantics.
80
- *
81
- * @param arrayPath the dot-delimited path of the top-level array.
82
- * @param array a reference to the top-level array.
83
- * @param fieldSchema schema of the array element or a field nested within it.
84
- * @param field reference to the array element or one of its subfields.
85
- * @param addToUpdateList callback to register modifications to this array.
86
- */
87
- function watchArrayForChanges(arrayPath, array, fieldSchema, field, addToUpdateList) {
88
- return new Proxy(field, {
89
- get(target, propertyKey) {
90
- const property = target[propertyKey];
91
- if (property instanceof Function) {
92
- // All methods of an array or its children are presumed to make
93
- // modifications, thus triggering an update of the full array.
94
- // TODO: #11 Only mark the change for mutating function calls.
95
- const result = (...args) => property.apply(field, args);
96
- addToUpdateList(arrayPath, array);
97
- return result;
98
- }
99
- if (typeof propertyKey === "symbol") {
100
- if (propertyKey === PROXY_TARGET_SYMBOL) {
101
- return target;
102
- }
103
- // Allow all other symbols to pass through.
104
- return property;
105
- }
106
- if (property instanceof Object) {
107
- // Wrap nested objects, including nested arrays, in child proxies.
108
- return watchArrayForChanges(arrayPath, array, getPropertySchema(field, fieldSchema, propertyKey), property, addToUpdateList);
109
- }
110
- // Otherwise we must be getting a primitive. No need to wrap it.
111
- return property;
112
- },
113
- set(target, propertyKey, value) {
114
- if (typeof propertyKey === "symbol") {
115
- // Allow symbols to pass through.
116
- return Reflect.set(target, propertyKey, value);
117
- }
118
- let processedValue = value;
119
- // If the new value is an object wrapped in a Firetender proxy, which can
120
- // commonly happen when referencing it inside a mutator function passed to
121
- // FiretenderDoc.prototype.update(), unwrap it.
122
- if (value instanceof Object) {
123
- const valueTarget = value[PROXY_TARGET_SYMBOL];
124
- if (valueTarget !== undefined) {
125
- processedValue = valueTarget;
126
- }
127
- }
128
- // An array element or one of its subfields is being set to a new value.
129
- // Parse the new value with the appropriate schema, set it in the local
130
- // data, and mark the entire top-level array as needing to be written.
131
- const propertySchema = getPropertySchema(field, fieldSchema, propertyKey);
132
- processedValue = propertySchema.parse(processedValue);
133
- let result = true;
134
- if (processedValue === undefined) {
135
- result = Reflect.deleteProperty(target, propertyKey);
136
- }
137
- else {
138
- result = Reflect.set(target, propertyKey, processedValue);
139
- }
140
- addToUpdateList(arrayPath, array);
141
- return result;
142
- },
143
- deleteProperty(target, propertyKey) {
144
- (0, ts_helpers_1.assertKeyIsString)(propertyKey);
145
- let result = true;
146
- if (target instanceof Array) {
147
- // Calling Reflect.deleteProperty on an array item sets it to undefined,
148
- // which causes Firestore writes to fail if ignoreUndefinedProperties is
149
- // not set, and which is generally not what we want. Hence splice.
150
- const index = Number(propertyKey);
151
- if (target.splice(index, 1).length !== 1) {
152
- throw RangeError(`Failed to delete array item with index ${propertyKey}. Out of bounds?`);
153
- }
154
- }
155
- else {
156
- result = Reflect.deleteProperty(target, propertyKey);
157
- }
158
- // Firestore's arrayRemove() deletes all matching entries, which is not
159
- // desired. So we have to rewrite the full array.
160
- addToUpdateList(arrayPath, array);
161
- return result;
162
- },
163
- });
164
- }
165
- exports.watchArrayForChanges = watchArrayForChanges;
166
- /**
167
- * Wraps an object based on a Zod schema in a proxy that watches for changes.
168
- *
169
- * Nested fields are monitored by creating a chain of proxies. Consider this
170
- * case:
171
- *
172
- * testDoc.w.record1.record2.someStringValue = "foo";
173
- *
174
- * There is a top-level proxy (call it proxy 0) for the ".w" accessor. Getting
175
- * ".record1" from it returns a child proxy (1) wrapping that field. Getting
176
- * ".record2" from that returns another child proxy (2) wrapping that subfield.
177
- * Finally, setting ".someStringValue" uses the setter of proxy 2 to set the new
178
- * value locally and register an update to be sent to Firestore.
179
- *
180
- * The proxy returned by watchFieldForChanges is used for fields and subfields
181
- * when no parent is an array. If a field or subfield is an array, the proxy
182
- * returned by watchArrayForChanges is used for it and all of its children,
183
- * regardless of whether they are arrays.
184
- *
185
- * @param fieldPath the dot-delimited path of the object being wrapped.
186
- * @param fieldSchema schema of the object's field.
187
- * @param field reference to the field in the local document data.
188
- * @param addToUpdateList callback to register modifications to this field.
189
- */
190
- function watchFieldForChanges(fieldPath, fieldSchema, field, addToUpdateList) {
191
- return new Proxy(field, {
192
- get(target, propertyKey) {
193
- const property = target[propertyKey];
194
- if (property instanceof Function) {
195
- // Provide methods with a "this" reference for the underlying field.
196
- return (...args) => property.apply(field, args);
197
- }
198
- if (typeof propertyKey === "symbol") {
199
- if (propertyKey === PROXY_TARGET_SYMBOL) {
200
- return target;
201
- }
202
- // Allow all other symbols to pass through.
203
- return property;
204
- }
205
- if (property instanceof Array) {
206
- // Wrap array subfields in the watchArrayForChanges proxy. It is
207
- // necessarily a top-level array, because otherwise we would be in
208
- // watchArrayForChanges already.
209
- return watchArrayForChanges([...fieldPath, propertyKey], property, getPropertySchema(field, fieldSchema, propertyKey), property, addToUpdateList);
210
- }
211
- if (property instanceof Object) {
212
- // Wrap nested objects in another instance of this proxy.
213
- return watchFieldForChanges([...fieldPath, propertyKey], getPropertySchema(field, fieldSchema, propertyKey), property, addToUpdateList);
214
- }
215
- // Otherwise we must be getting a primitive. No need to wrap it.
216
- return property;
217
- },
218
- set(target, propertyKey, value) {
219
- if (typeof propertyKey === "symbol") {
220
- // Allow symbols to pass through.
221
- return Reflect.set(target, propertyKey, value);
222
- }
223
- let processedValue = value;
224
- // If the new value is an object wrapped in a Firetender proxy, which can
225
- // commonly happen when referencing it inside a mutator function passed to
226
- // FiretenderDoc.prototype.update(), unwrap it.
227
- if (value instanceof Object) {
228
- const valueTarget = value[PROXY_TARGET_SYMBOL];
229
- if (valueTarget !== undefined) {
230
- processedValue = valueTarget;
231
- }
232
- }
233
- // A property of this object is being set to a new value. Parse the new
234
- // value with the appropriate schema, set it in the local data, and mark
235
- // the entire top-level array as needing to be written. If the new value
236
- // is undefined, delete the property.
237
- const propertySchema = getPropertySchema(field, fieldSchema, propertyKey);
238
- processedValue = propertySchema.parse(processedValue);
239
- if (processedValue === undefined) {
240
- addToUpdateList([...fieldPath, propertyKey], (0, firestore_deps_1.deleteField)());
241
- return Reflect.deleteProperty(target, propertyKey);
242
- }
243
- else {
244
- addToUpdateList([...fieldPath, propertyKey], processedValue);
245
- return Reflect.set(target, propertyKey, processedValue);
246
- }
247
- },
248
- deleteProperty(target, propertyKey) {
249
- (0, ts_helpers_1.assertKeyIsString)(propertyKey);
250
- // Delete the field in Firestore by marking it with the deleteField
251
- // operator.
252
- addToUpdateList([...fieldPath, propertyKey], (0, firestore_deps_1.deleteField)());
253
- return Reflect.deleteProperty(target, propertyKey);
254
- },
255
- });
256
- }
257
- exports.watchFieldForChanges = watchFieldForChanges;
258
- //# sourceMappingURL=proxies.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"proxies.js","sourceRoot":"","sources":["../src/proxies.ts"],"names":[],"mappings":"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"}
package/src/proxies.ts DELETED
@@ -1,299 +0,0 @@
1
- import { z } from "zod";
2
-
3
- import { deleteField } from "./firestore-deps";
4
- import { assertKeyIsString } from "./ts-helpers";
5
-
6
- /**
7
- * Given a Zod schema representing a collection, returns the sub-schema of the
8
- * specified property.
9
- */
10
- function getPropertySchema(
11
- parent: any,
12
- parentSchema: z.ZodTypeAny,
13
- propertyKey: string
14
- ): z.ZodTypeAny {
15
- let schema: any = parentSchema;
16
- // eslint-disable-next-line no-constant-condition
17
- while (true) {
18
- switch (schema._def.typeName) {
19
- // If the schema object is wrapped (e.g., by being optional or having a
20
- // default), unwrap it until we get to the underlying collection type.
21
- case z.ZodFirstPartyTypeKind.ZodOptional:
22
- case z.ZodFirstPartyTypeKind.ZodNullable:
23
- schema = schema.unwrap();
24
- continue;
25
- case z.ZodFirstPartyTypeKind.ZodDefault:
26
- schema = schema.removeDefault();
27
- continue;
28
- case z.ZodFirstPartyTypeKind.ZodEffects:
29
- schema = schema.innerType();
30
- continue;
31
- // Return the sub-schemas of supported collection types.
32
- case z.ZodFirstPartyTypeKind.ZodRecord:
33
- if (
34
- schema.keySchema._def.typeName !== z.ZodFirstPartyTypeKind.ZodString
35
- ) {
36
- throw TypeError(
37
- `The ZodRecord for property ${propertyKey} has keys of type ${schema.keySchema._def.typeName}. Only strings are supported.`
38
- );
39
- }
40
- return schema.valueSchema;
41
- case z.ZodFirstPartyTypeKind.ZodArray:
42
- return schema.element;
43
- case z.ZodFirstPartyTypeKind.ZodObject:
44
- return schema.shape[propertyKey];
45
- case z.ZodFirstPartyTypeKind.ZodDiscriminatedUnion:
46
- return (schema as any).optionsMap.get(
47
- parent[(schema as any).discriminator]
48
- );
49
- // If the parent is of type ZodAny, so are its properties.
50
- case z.ZodFirstPartyTypeKind.ZodAny:
51
- return z.any();
52
- default:
53
- throw TypeError(
54
- `Unsupported schema type for property "${propertyKey}": ${schema._def.typeName}`
55
- );
56
- }
57
- }
58
- }
59
-
60
- /**
61
- * Getting this symbol from one of our proxies returns the proxy's target.
62
- */
63
- const PROXY_TARGET_SYMBOL = Symbol("proxy_target");
64
-
65
- /**
66
- * Wraps a top-level array, its elements, or its elements' subfields in a proxy
67
- * that watches for changes.
68
- *
69
- * Firestore supports limited modification of arrays; see the 2018 blog entry at
70
- * https://firebase.blog/posts/2018/08/better-arrays-in-cloud-firestore for
71
- * details and some history. The tl;dr is that we can't update array entries;
72
- * we can only append or remove them.
73
- *
74
- * For this reason, this proxy only works with top-level arrays: the wrapped
75
- * array cannot be inside a parent array, though it may be in a record or nested
76
- * records. Child proxies maintain a link to the top-level array and trigger a
77
- * rewrite of the whole thing if there are any changes to the contents. As a
78
- * consequence, the watchFieldForChanges proxy (below) can produce child
79
- * watchArrayForChanges proxies, but all children of watchArrayForChanges will
80
- * always use the watchArrayForChanges proxy to maintain a reference to the
81
- * top-level array.
82
- *
83
- * Appending an element to the top-level array could in theory be supported, but
84
- * Firestore's arrayUnion operator only appends entries that don't already
85
- * exist. That is not how Javascript arrays work, so to reduce logical overhead
86
- * we don't bother with it.
87
- *
88
- * Deletion of entries in the top-level array are handled using Firestore's
89
- * arrayRemove operator. The deleted element gets removed entirely, so the
90
- * resulting array remains dense. Note that this behavior differs from how
91
- * Javascript's delete operator normally works with arrays, but here we favor
92
- * Firestore's array semantics.
93
- *
94
- * @param arrayPath the dot-delimited path of the top-level array.
95
- * @param array a reference to the top-level array.
96
- * @param fieldSchema schema of the array element or a field nested within it.
97
- * @param field reference to the array element or one of its subfields.
98
- * @param addToUpdateList callback to register modifications to this array.
99
- */
100
- export function watchArrayForChanges<
101
- ArrayElementType,
102
- FieldSchemaType extends z.ZodTypeAny
103
- >(
104
- arrayPath: string[],
105
- array: ArrayElementType[],
106
- fieldSchema: FieldSchemaType,
107
- field: z.infer<FieldSchemaType>,
108
- addToUpdateList: (path: string[], newValue: any) => void
109
- ): z.infer<FieldSchemaType> {
110
- return new Proxy(field, {
111
- get(target, propertyKey) {
112
- const property = target[propertyKey];
113
- if (property instanceof Function) {
114
- // All methods of an array or its children are presumed to make
115
- // modifications, thus triggering an update of the full array.
116
- // TODO: #11 Only mark the change for mutating function calls.
117
- const result = (...args: any[]) => property.apply(field, args);
118
- addToUpdateList(arrayPath, array);
119
- return result;
120
- }
121
- if (typeof propertyKey === "symbol") {
122
- if (propertyKey === PROXY_TARGET_SYMBOL) {
123
- return target;
124
- }
125
- // Allow all other symbols to pass through.
126
- return property;
127
- }
128
- if (property instanceof Object) {
129
- // Wrap nested objects, including nested arrays, in child proxies.
130
- return watchArrayForChanges(
131
- arrayPath,
132
- array,
133
- getPropertySchema(field, fieldSchema, propertyKey),
134
- property,
135
- addToUpdateList
136
- );
137
- }
138
- // Otherwise we must be getting a primitive. No need to wrap it.
139
- return property;
140
- },
141
- set(target, propertyKey, value) {
142
- if (typeof propertyKey === "symbol") {
143
- // Allow symbols to pass through.
144
- return Reflect.set(target, propertyKey, value);
145
- }
146
- let processedValue = value;
147
- // If the new value is an object wrapped in a Firetender proxy, which can
148
- // commonly happen when referencing it inside a mutator function passed to
149
- // FiretenderDoc.prototype.update(), unwrap it.
150
- if (value instanceof Object) {
151
- const valueTarget = value[PROXY_TARGET_SYMBOL];
152
- if (valueTarget !== undefined) {
153
- processedValue = valueTarget;
154
- }
155
- }
156
- // An array element or one of its subfields is being set to a new value.
157
- // Parse the new value with the appropriate schema, set it in the local
158
- // data, and mark the entire top-level array as needing to be written.
159
- const propertySchema = getPropertySchema(field, fieldSchema, propertyKey);
160
- processedValue = propertySchema.parse(processedValue);
161
- let result = true;
162
- if (processedValue === undefined) {
163
- result = Reflect.deleteProperty(target, propertyKey);
164
- } else {
165
- result = Reflect.set(target, propertyKey, processedValue);
166
- }
167
- addToUpdateList(arrayPath, array);
168
- return result;
169
- },
170
- deleteProperty(target, propertyKey) {
171
- assertKeyIsString(propertyKey);
172
- let result = true;
173
- if ((target as any) instanceof Array) {
174
- // Calling Reflect.deleteProperty on an array item sets it to undefined,
175
- // which causes Firestore writes to fail if ignoreUndefinedProperties is
176
- // not set, and which is generally not what we want. Hence splice.
177
- const index = Number(propertyKey);
178
- if (target.splice(index, 1).length !== 1) {
179
- throw RangeError(
180
- `Failed to delete array item with index ${propertyKey}. Out of bounds?`
181
- );
182
- }
183
- } else {
184
- result = Reflect.deleteProperty(target, propertyKey);
185
- }
186
- // Firestore's arrayRemove() deletes all matching entries, which is not
187
- // desired. So we have to rewrite the full array.
188
- addToUpdateList(arrayPath, array);
189
- return result;
190
- },
191
- });
192
- }
193
-
194
- /**
195
- * Wraps an object based on a Zod schema in a proxy that watches for changes.
196
- *
197
- * Nested fields are monitored by creating a chain of proxies. Consider this
198
- * case:
199
- *
200
- * testDoc.w.record1.record2.someStringValue = "foo";
201
- *
202
- * There is a top-level proxy (call it proxy 0) for the ".w" accessor. Getting
203
- * ".record1" from it returns a child proxy (1) wrapping that field. Getting
204
- * ".record2" from that returns another child proxy (2) wrapping that subfield.
205
- * Finally, setting ".someStringValue" uses the setter of proxy 2 to set the new
206
- * value locally and register an update to be sent to Firestore.
207
- *
208
- * The proxy returned by watchFieldForChanges is used for fields and subfields
209
- * when no parent is an array. If a field or subfield is an array, the proxy
210
- * returned by watchArrayForChanges is used for it and all of its children,
211
- * regardless of whether they are arrays.
212
- *
213
- * @param fieldPath the dot-delimited path of the object being wrapped.
214
- * @param fieldSchema schema of the object's field.
215
- * @param field reference to the field in the local document data.
216
- * @param addToUpdateList callback to register modifications to this field.
217
- */
218
- export function watchFieldForChanges<FieldSchemaType extends z.ZodTypeAny>(
219
- fieldPath: string[],
220
- fieldSchema: FieldSchemaType,
221
- field: z.infer<FieldSchemaType>,
222
- addToUpdateList: (path: string[], newValue: any) => void
223
- ): z.infer<FieldSchemaType> {
224
- return new Proxy(field, {
225
- get(target, propertyKey) {
226
- const property = target[propertyKey];
227
- if (property instanceof Function) {
228
- // Provide methods with a "this" reference for the underlying field.
229
- return (...args: any[]) => property.apply(field, args);
230
- }
231
- if (typeof propertyKey === "symbol") {
232
- if (propertyKey === PROXY_TARGET_SYMBOL) {
233
- return target;
234
- }
235
- // Allow all other symbols to pass through.
236
- return property;
237
- }
238
- if (property instanceof Array) {
239
- // Wrap array subfields in the watchArrayForChanges proxy. It is
240
- // necessarily a top-level array, because otherwise we would be in
241
- // watchArrayForChanges already.
242
- return watchArrayForChanges(
243
- [...fieldPath, propertyKey],
244
- property,
245
- getPropertySchema(field, fieldSchema, propertyKey),
246
- property,
247
- addToUpdateList
248
- );
249
- }
250
- if (property instanceof Object) {
251
- // Wrap nested objects in another instance of this proxy.
252
- return watchFieldForChanges(
253
- [...fieldPath, propertyKey],
254
- getPropertySchema(field, fieldSchema, propertyKey),
255
- property,
256
- addToUpdateList
257
- );
258
- }
259
- // Otherwise we must be getting a primitive. No need to wrap it.
260
- return property;
261
- },
262
- set(target, propertyKey, value) {
263
- if (typeof propertyKey === "symbol") {
264
- // Allow symbols to pass through.
265
- return Reflect.set(target, propertyKey, value);
266
- }
267
- let processedValue = value;
268
- // If the new value is an object wrapped in a Firetender proxy, which can
269
- // commonly happen when referencing it inside a mutator function passed to
270
- // FiretenderDoc.prototype.update(), unwrap it.
271
- if (value instanceof Object) {
272
- const valueTarget = value[PROXY_TARGET_SYMBOL];
273
- if (valueTarget !== undefined) {
274
- processedValue = valueTarget;
275
- }
276
- }
277
- // A property of this object is being set to a new value. Parse the new
278
- // value with the appropriate schema, set it in the local data, and mark
279
- // the entire top-level array as needing to be written. If the new value
280
- // is undefined, delete the property.
281
- const propertySchema = getPropertySchema(field, fieldSchema, propertyKey);
282
- processedValue = propertySchema.parse(processedValue);
283
- if (processedValue === undefined) {
284
- addToUpdateList([...fieldPath, propertyKey], deleteField());
285
- return Reflect.deleteProperty(target, propertyKey);
286
- } else {
287
- addToUpdateList([...fieldPath, propertyKey], processedValue);
288
- return Reflect.set(target, propertyKey, processedValue);
289
- }
290
- },
291
- deleteProperty(target, propertyKey) {
292
- assertKeyIsString(propertyKey);
293
- // Delete the field in Firestore by marking it with the deleteField
294
- // operator.
295
- addToUpdateList([...fieldPath, propertyKey], deleteField());
296
- return Reflect.deleteProperty(target, propertyKey);
297
- },
298
- });
299
- }