Skip to content

Commit dccd23d

Browse files
Tree-shake ValidationPath (#4594)
1 parent 82ea467 commit dccd23d

File tree

2 files changed

+75
-66
lines changed

2 files changed

+75
-66
lines changed

packages/database/src/core/util/Path.ts

Lines changed: 57 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
import { nameCompare } from './util';
1919
import { stringLength } from '@firebase/util';
2020

21+
/** Maximum key depth. */
22+
const MAX_PATH_DEPTH = 32;
23+
24+
/** Maximum number of (UTF8) bytes in a Firebase path. */
25+
const MAX_PATH_LENGTH_BYTES = 768;
26+
2127
/**
2228
* An immutable object representing a parsed path. It's immutable so that you
2329
* can pass them around to other functions without worrying about them changing
@@ -252,84 +258,78 @@ export function pathContains(path: Path, other: Path): boolean {
252258
* The definition of a path always begins with '/'.
253259
*/
254260
export class ValidationPath {
255-
private parts_: string[];
261+
parts_: string[];
256262
/** Initialize to number of '/' chars needed in path. */
257-
private byteLength_: number;
263+
byteLength_: number;
258264

259265
/**
260266
* @param path Initial Path.
261267
* @param errorPrefix_ Prefix for any error messages.
262268
*/
263-
constructor(path: Path, private errorPrefix_: string) {
269+
constructor(path: Path, public errorPrefix_: string) {
264270
this.parts_ = pathSlice(path, 0);
265271
/** Initialize to number of '/' chars needed in path. */
266272
this.byteLength_ = Math.max(1, this.parts_.length);
267273

268274
for (let i = 0; i < this.parts_.length; i++) {
269275
this.byteLength_ += stringLength(this.parts_[i]);
270276
}
271-
this.checkValid_();
277+
validationPathCheckValid(this);
272278
}
279+
}
273280

274-
/** @const {number} Maximum key depth. */
275-
static get MAX_PATH_DEPTH() {
276-
return 32;
281+
export function validationPathPush(
282+
validationPath: ValidationPath,
283+
child: string
284+
): void {
285+
// Count the needed '/'
286+
if (validationPath.parts_.length > 0) {
287+
validationPath.byteLength_ += 1;
277288
}
289+
validationPath.parts_.push(child);
290+
validationPath.byteLength_ += stringLength(child);
291+
validationPathCheckValid(validationPath);
292+
}
278293

279-
/** @const {number} Maximum number of (UTF8) bytes in a Firebase path. */
280-
static get MAX_PATH_LENGTH_BYTES() {
281-
return 768;
282-
}
283-
284-
/** @param child */
285-
push(child: string) {
286-
// Count the needed '/'
287-
if (this.parts_.length > 0) {
288-
this.byteLength_ += 1;
289-
}
290-
this.parts_.push(child);
291-
this.byteLength_ += stringLength(child);
292-
this.checkValid_();
294+
export function validationPathPop(validationPath: ValidationPath): void {
295+
const last = validationPath.parts_.pop();
296+
validationPath.byteLength_ -= stringLength(last);
297+
// Un-count the previous '/'
298+
if (validationPath.parts_.length > 0) {
299+
validationPath.byteLength_ -= 1;
293300
}
301+
}
294302

295-
pop() {
296-
const last = this.parts_.pop();
297-
this.byteLength_ -= stringLength(last);
298-
// Un-count the previous '/'
299-
if (this.parts_.length > 0) {
300-
this.byteLength_ -= 1;
301-
}
303+
function validationPathCheckValid(validationPath: ValidationPath): void {
304+
if (validationPath.byteLength_ > MAX_PATH_LENGTH_BYTES) {
305+
throw new Error(
306+
validationPath.errorPrefix_ +
307+
'has a key path longer than ' +
308+
MAX_PATH_LENGTH_BYTES +
309+
' bytes (' +
310+
validationPath.byteLength_ +
311+
').'
312+
);
302313
}
303-
304-
private checkValid_() {
305-
if (this.byteLength_ > ValidationPath.MAX_PATH_LENGTH_BYTES) {
306-
throw new Error(
307-
this.errorPrefix_ +
308-
'has a key path longer than ' +
309-
ValidationPath.MAX_PATH_LENGTH_BYTES +
310-
' bytes (' +
311-
this.byteLength_ +
312-
').'
313-
);
314-
}
315-
if (this.parts_.length > ValidationPath.MAX_PATH_DEPTH) {
316-
throw new Error(
317-
this.errorPrefix_ +
318-
'path specified exceeds the maximum depth that can be written (' +
319-
ValidationPath.MAX_PATH_DEPTH +
320-
') or object contains a cycle ' +
321-
this.toErrorString()
322-
);
323-
}
314+
if (validationPath.parts_.length > MAX_PATH_DEPTH) {
315+
throw new Error(
316+
validationPath.errorPrefix_ +
317+
'path specified exceeds the maximum depth that can be written (' +
318+
MAX_PATH_DEPTH +
319+
') or object contains a cycle ' +
320+
validationPathToErrorString(validationPath)
321+
);
324322
}
323+
}
325324

326-
/**
327-
* String for use in error messages - uses '.' notation for path.
328-
*/
329-
toErrorString(): string {
330-
if (this.parts_.length === 0) {
331-
return '';
332-
}
333-
return "in property '" + this.parts_.join('.') + "'";
325+
/**
326+
* String for use in error messages - uses '.' notation for path.
327+
*/
328+
export function validationPathToErrorString(
329+
validationPath: ValidationPath
330+
): string {
331+
if (validationPath.parts_.length === 0) {
332+
return '';
334333
}
334+
return "in property '" + validationPath.parts_.join('.') + "'";
335335
}

packages/database/src/core/util/validation.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ import {
2323
pathGetBack,
2424
pathGetFront,
2525
pathSlice,
26-
ValidationPath
26+
ValidationPath,
27+
validationPathPop,
28+
validationPathPush,
29+
validationPathToErrorString
2730
} from './Path';
2831
import {
2932
contains,
@@ -119,20 +122,26 @@ export const validateFirebaseData = function (
119122
path_ instanceof Path ? new ValidationPath(path_, errorPrefix) : path_;
120123

121124
if (data === undefined) {
122-
throw new Error(errorPrefix + 'contains undefined ' + path.toErrorString());
125+
throw new Error(
126+
errorPrefix + 'contains undefined ' + validationPathToErrorString(path)
127+
);
123128
}
124129
if (typeof data === 'function') {
125130
throw new Error(
126131
errorPrefix +
127132
'contains a function ' +
128-
path.toErrorString() +
133+
validationPathToErrorString(path) +
129134
' with contents = ' +
130135
data.toString()
131136
);
132137
}
133138
if (isInvalidJSONNumber(data)) {
134139
throw new Error(
135-
errorPrefix + 'contains ' + data.toString() + ' ' + path.toErrorString()
140+
errorPrefix +
141+
'contains ' +
142+
data.toString() +
143+
' ' +
144+
validationPathToErrorString(path)
136145
);
137146
}
138147

@@ -147,7 +156,7 @@ export const validateFirebaseData = function (
147156
'contains a string greater than ' +
148157
MAX_LEAF_SIZE_ +
149158
' utf8 bytes ' +
150-
path.toErrorString() +
159+
validationPathToErrorString(path) +
151160
" ('" +
152161
data.substring(0, 50) +
153162
"...')"
@@ -170,23 +179,23 @@ export const validateFirebaseData = function (
170179
' contains an invalid key (' +
171180
key +
172181
') ' +
173-
path.toErrorString() +
182+
validationPathToErrorString(path) +
174183
'. Keys must be non-empty strings ' +
175184
'and can\'t contain ".", "#", "$", "/", "[", or "]"'
176185
);
177186
}
178187
}
179188

180-
path.push(key);
189+
validationPathPush(path, key);
181190
validateFirebaseData(errorPrefix, value, path);
182-
path.pop();
191+
validationPathPop(path);
183192
});
184193

185194
if (hasDotValue && hasActualChild) {
186195
throw new Error(
187196
errorPrefix +
188197
' contains ".value" child ' +
189-
path.toErrorString() +
198+
validationPathToErrorString(path) +
190199
' in addition to actual children.'
191200
);
192201
}

0 commit comments

Comments
 (0)