@dereekb/firebase 13.12.1 → 13.12.2

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.
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@dereekb/firebase/eslint",
3
- "version": "13.12.1",
3
+ "version": "13.12.2",
4
4
  "peerDependencies": {
5
5
  "@typescript-eslint/utils": "8.59.3"
6
6
  },
7
7
  "devDependencies": {
8
- "@dereekb/util": "13.12.1",
8
+ "@dereekb/util": "13.12.2",
9
9
  "@marcbachmann/cel-js": "^7.6.1",
10
10
  "@typescript-eslint/parser": "8.59.3",
11
11
  "eslint": "10.4.0"
@@ -14,4 +14,4 @@ export { FIREBASE_REQUIRE_DBX_MODEL_COMPANION_TAGS_RULE, type FirebaseRequireDbx
14
14
  export { parseStorageRules, MIRRORS_POLICY_KEY_MARKER_REGEX, type ParsedRuleBranch, type ParsedStorageRulesBlock } from './storage-rules-parser';
15
15
  export { parseFirestoreRules, type ParsedFirestoreMatchBlock } from './firestore-rules-parser';
16
16
  export { FIREBASE_ESLINT_PLUGIN, firebaseESLintPlugin, type FirebaseEslintPlugin } from './plugin';
17
- export { DBX_MODEL_FIREBASE_INDEX_MARKER, DEFAULT_CONSTRAINT_FACTORY_NAMES, DEFAULT_INDEX_AFFECTING_CONSTRAINT_NAMES, DEFAULT_PAGINATION_CONSTRAINT_NAMES, FIREBASE_MODULE, QUERY_SUFFIX, DEFAULT_CRUD_FUNCTION_TYPE_VERBS, DEFAULT_API_DETAILS_FACTORY_NAME, INPUT_TYPE_PROPERTY_NAME, API_DETAILS_IMPORT_MODULE } from './util';
17
+ export { DBX_MODEL_FIREBASE_INDEX_MARKER, DEFAULT_CONSTRAINT_FACTORY_NAMES, DEFAULT_DISCOVERY_EXCLUDED_DIRS, DEFAULT_INDEX_AFFECTING_CONSTRAINT_NAMES, DEFAULT_PAGINATION_CONSTRAINT_NAMES, FIREBASE_MODULE, QUERY_SUFFIX, DEFAULT_CRUD_FUNCTION_TYPE_VERBS, DEFAULT_API_DETAILS_FACTORY_NAME, INPUT_TYPE_PROPERTY_NAME, API_DETAILS_IMPORT_MODULE, discoveryGlobExcludeFilter } from './util';
@@ -1,4 +1,4 @@
1
- import type { AstNode } from './util';
1
+ import { type AstNode } from './util';
2
2
  /**
3
3
  * Default file name searched relative to the lint root when `firestoreRulesPath` is omitted.
4
4
  */
@@ -1,8 +1,14 @@
1
- import type { AstNode } from './util';
1
+ import { type AstNode } from './util';
2
2
  /**
3
- * Default glob patterns (relative to ESLint `cwd`) used to locate model + factory source files.
4
- * Mirrors {@link require-firestore-rule-for-service-model}'s search roots so the orphan rule
5
- * picks up factories declared anywhere a model interface might also live.
3
+ * Default glob pattern (relative to ESLint `cwd`) used to locate `@dbxModelServiceFactory` source
4
+ * files. A single layout-agnostic `**\/*.ts` scan finds a consumer's own factory declarations no
5
+ * matter where they live, rather than assuming the dbx-components monorepo layout
6
+ * (`components/*\/src/lib/model`, `apps/*\/src/app`) — which a downstream consumer of
7
+ * `@dereekb/firebase` does not share. The walk prunes `node_modules`/build dirs
8
+ * (see {@link discoveryGlobExcludeFilter}) and the cheap `text.includes('@'+factoryTag)` pre-filter
9
+ * in {@link collectFactoryModelTypesFromFile} keeps it from parsing files that carry no tag, so the
10
+ * broad glob stays fast even in a large consumer repo. In-repo this is a superset of the former
11
+ * monorepo-specific roots, so the demo app's framework + local factories still resolve.
6
12
  */
7
13
  export declare const DEFAULT_FACTORY_SEARCH_ROOTS: readonly string[];
8
14
  /**
@@ -1,4 +1,6 @@
1
+ import type { Maybe } from '@dereekb/util';
1
2
  import { type AstNode } from './util';
3
+ import { type ParserServicesLike } from './storagefile-import-resolver';
2
4
  /**
3
5
  * Default type name the rule looks for on top-level declarators. Variables whose type
4
6
  * annotation resolves to this identifier are treated as upload policies and validated
@@ -27,6 +29,12 @@ export interface FirebaseRequireStorageFilePolicyMatchesRulesRuleOptions {
27
29
  * {@link DEFAULT_STORAGE_FILE_UPLOAD_POLICY_TYPE_NAME}.
28
30
  */
29
31
  readonly policyTypeName?: string;
32
+ /**
33
+ * Policies whose `buildUploadPath` is legitimately dynamic (e.g. injects a runtime timestamp)
34
+ * and cannot be statically folded. Each entry matches a policy's `purpose` key or its
35
+ * declarator name; matching policies are skipped instead of reporting `unresolvablePolicyPath`.
36
+ */
37
+ readonly allowUnresolvablePolicies?: readonly string[];
30
38
  }
31
39
  /**
32
40
  * ESLint rule definition for require-storagefile-policy-matches-rules.
@@ -47,6 +55,10 @@ export interface FirebaseRequireStorageFilePolicyMatchesRulesRuleDefinition {
47
55
  interface RuleContext {
48
56
  readonly options: FirebaseRequireStorageFilePolicyMatchesRulesRuleOptions[];
49
57
  readonly cwd?: string;
58
+ readonly sourceCode?: {
59
+ readonly parserServices?: Maybe<ParserServicesLike>;
60
+ };
61
+ readonly parserServices?: Maybe<ParserServicesLike>;
50
62
  readonly report: (descriptor: {
51
63
  node: AstNode;
52
64
  messageId: string;
@@ -55,17 +67,25 @@ interface RuleContext {
55
67
  }
56
68
  /**
57
69
  * ESLint rule that cross-checks every `StorageFilePurposeUploadPolicy`-typed declaration in
58
- * a `*-firebase` component against the workspace's `storage.rules`. Each policy must have a
59
- * paired `// Mirrors STORAGE_FILE_PURPOSE_UPLOAD_POLICIES[<KEY>]` match block whose
60
- * `request.resource.size` cap and `request.resource.contentType` predicate are at least as
61
- * permissive as the TypeScript policy's `maxFileSizeBytes` and `allowedMimeTypes`.
70
+ * a `*-firebase` component against the workspace's `storage.rules`. The policy's
71
+ * `buildUploadPath` builder is statically folded to a concrete path template (an ordered list
72
+ * of literal / wildcard segments) and paired with the `storage.rules` `allow write` match
73
+ * block at the same path. On the paired block the `request.resource.size` cap and
74
+ * `request.resource.contentType` predicate must be at least as permissive as the policy's
75
+ * `maxFileSizeBytes` and `allowedMimeTypes`.
76
+ *
77
+ * Pairing is derived from the resolved path — not a marker comment — so the linker proves the
78
+ * policy's actual upload path lands in the rules block rather than trusting an unchecked
79
+ * assertion. When the builder cannot be folded (unknown const, unmodeled call, runtime value)
80
+ * the rule reports `unresolvablePolicyPath` and never guesses; genuinely dynamic builders opt
81
+ * out via `allowUnresolvablePolicies`.
62
82
  *
63
83
  * Reports on the TS side so drift surfaces in the normal lint pipeline; mismatches almost
64
84
  * always originate from editing one side and forgetting the other.
65
85
  *
66
86
  * @example
67
87
  * ```ts
68
- * // OK — storage.rules has `Mirrors ...[USER_AVATAR_PURPOSE]` block with matching constraints
88
+ * // OK — storage.rules has `match /uploads/u/{uid}/avatar.img` with matching constraints
69
89
  * export const USER_AVATAR_UPLOAD_POLICY: StorageFilePurposeUploadPolicy = {
70
90
  * purpose: USER_AVATAR_PURPOSE,
71
91
  * allowedMimeTypes: ['image/jpeg', 'image/png'],
@@ -1,4 +1,6 @@
1
+ import type { Maybe } from '@dereekb/util';
1
2
  import { type PredicateBranch } from './predicate-evaluator';
3
+ import type { FoldedPathSegment } from './storagefile-path-fold';
2
4
  /**
3
5
  * Marker comment that pairs a `storage.rules` match block with a TypeScript policy key
4
6
  * in `STORAGE_FILE_PURPOSE_UPLOAD_POLICIES`. The capture group is the policy key constant
@@ -14,13 +16,15 @@ export declare const MIRRORS_POLICY_KEY_MARKER_REGEX: RegExp;
14
16
  */
15
17
  export type ParsedRuleBranch = PredicateBranch;
16
18
  /**
17
- * One `match /<path>` block in `storage.rules` paired with a `// Mirrors ...` marker.
18
- * `branches` carries the disjunction of (size, MIME) tuples extracted from the block's
19
- * `allow write` predicate; `unsupported` is set when the parser cannot reduce the
20
- * predicate to >=1 valid branch.
19
+ * One leaf `match /<path>` block in `storage.rules` that carries an `allow write|create|update`
20
+ * predicate. `branches` carries the disjunction of (size, MIME) tuples extracted from the
21
+ * predicate; `unsupported` is set when the parser cannot reduce the predicate to >=1 valid
22
+ * branch. `matchPath` is the full accumulated path stack used to pair the block with a folded
23
+ * upload-policy path. `mirrorsPolicyKey` is the legacy `// Mirrors ...` marker key when present
24
+ * — purely informational now that pairing is by path, retained for backward compatibility.
21
25
  */
22
26
  export interface ParsedStorageRulesBlock {
23
- readonly mirrorsPolicyKey: string;
27
+ readonly mirrorsPolicyKey?: Maybe<string>;
24
28
  readonly matchPath: string;
25
29
  readonly branches: readonly ParsedRuleBranch[];
26
30
  readonly sourceLine: number;
@@ -28,11 +32,23 @@ export interface ParsedStorageRulesBlock {
28
32
  readonly unsupported?: string;
29
33
  }
30
34
  /**
31
- * Parses a `storage.rules` source string and returns every match block paired with a
32
- * `// Mirrors STORAGE_FILE_PURPOSE_UPLOAD_POLICIES[<KEY>]` marker. Catch-all deny blocks
33
- * are skipped; the rest of the tree is walked normally.
35
+ * Converts a parsed leaf block's `matchPath` (e.g. `/uploads/u/{uid}/jr/{shortKey}`) into a
36
+ * structural segment list for comparison against a folded upload-policy path. Empty segments
37
+ * (leading/trailing/duplicate slashes) are dropped; `{var}` / `{var=**}` path variables become
38
+ * wildcards; every other segment is a literal.
39
+ *
40
+ * @param matchPath - The accumulated match-path string.
41
+ * @returns The structural segment list.
42
+ */
43
+ export declare function rulesMatchPathToSegments(matchPath: string): FoldedPathSegment[];
44
+ /**
45
+ * Parses a `storage.rules` source string and returns every leaf `match` block that carries an
46
+ * `allow write|create|update` predicate, each with its full accumulated `matchPath`. Catch-all
47
+ * deny blocks are skipped; the rest of the tree is walked normally. Pairing with TypeScript
48
+ * upload policies is by path (see {@link rulesMatchPathToSegments}); the `// Mirrors ...` marker
49
+ * is no longer required.
34
50
  *
35
51
  * @param source - The raw rules source text.
36
- * @returns Parsed mirrored blocks in source order.
52
+ * @returns Parsed leaf blocks in source order.
37
53
  */
38
54
  export declare function parseStorageRules(source: string): ParsedStorageRulesBlock[];
@@ -0,0 +1,33 @@
1
+ import type { Maybe } from '@dereekb/util';
2
+ import { type AstNode } from './util';
3
+ import { type ImportedBindingResolver } from './storagefile-path-fold';
4
+ /**
5
+ * The slice of `@typescript-eslint` parser services the cross-module resolver needs: the TS
6
+ * `Program` and the ESTree→TS node map. Typed loosely so the resolver stays decoupled from a
7
+ * specific `@typescript-eslint` version.
8
+ */
9
+ export interface ParserServicesLike {
10
+ readonly program?: {
11
+ getTypeChecker(): unknown;
12
+ };
13
+ readonly esTreeNodeToTSNodeMap?: {
14
+ get(node: AstNode): unknown;
15
+ };
16
+ }
17
+ /**
18
+ * Builds an {@link ImportedBindingResolver} backed by the TypeScript type checker, or null when
19
+ * type information is unavailable (e.g. a lint config without `parserOptions.project`, or a
20
+ * pure-AST test harness). The resolver performs a single hop: it maps an imported identifier in
21
+ * the linted file to the declaration's source file, re-parses that file to ESTree (cached), and
22
+ * returns the named binding plus that module's scope. Identifiers local to the resolved module
23
+ * fold via the re-parsed program directly; transitive re-imports from a third module are not
24
+ * followed.
25
+ *
26
+ * Every failure mode (no symbol, unreadable file, parse error, identifier from an already
27
+ * re-parsed module) returns null so the analyzer falls back to reporting an unresolvable path —
28
+ * it never throws into the lint pass.
29
+ *
30
+ * @param services - The parser services, when present.
31
+ * @returns A resolver, or null when type information is unavailable.
32
+ */
33
+ export declare function createImportedBindingResolver(services: Maybe<ParserServicesLike>): Maybe<ImportedBindingResolver>;
@@ -0,0 +1,121 @@
1
+ import type { Maybe } from '@dereekb/util';
2
+ import { type AstNode, type ImportRegistry } from './util';
3
+ /**
4
+ * One segment of a folded upload path. A `literal` segment carries its fixed text (e.g.
5
+ * `uploads`, `jr`, `photo.img`); a `wildcard` segment stands in for any single path segment
6
+ * (a destructured param, a positional argument, a `mergeSlashPaths` variadic element) and
7
+ * compares equal to a Firebase rules `{var}` / `{var=**}` path variable.
8
+ */
9
+ export interface FoldedPathSegment {
10
+ readonly kind: 'literal' | 'wildcard';
11
+ readonly value: string;
12
+ }
13
+ /**
14
+ * The result of statically folding a `buildUploadPath` builder: an ordered list of
15
+ * {@link FoldedPathSegment}s with leading/trailing/duplicate `/` collapsed.
16
+ */
17
+ export interface FoldedUploadPath {
18
+ readonly segments: readonly FoldedPathSegment[];
19
+ }
20
+ /**
21
+ * Outcome of {@link foldUploadPath}: either a folded path or a human-readable reason the
22
+ * builder could not be reduced to a constant path template. The rule reports the reason via
23
+ * the `unresolvablePolicyPath` diagnostic so authors either make the builder foldable or opt
24
+ * out explicitly — the analyzer never guesses.
25
+ */
26
+ export type FoldUploadPathResult = {
27
+ readonly ok: true;
28
+ readonly path: FoldedUploadPath;
29
+ } | {
30
+ readonly ok: false;
31
+ readonly reason: string;
32
+ };
33
+ /**
34
+ * Resolves an imported binding (const declarator or function/arrow declaration) to its
35
+ * declaration node in another module, plus the scope that node lives in so further
36
+ * identifiers inside it resolve against that module's own imports/consts.
37
+ *
38
+ * Supplied by the rule when type information is available; absent in pure-AST contexts
39
+ * (e.g. the unit tests), in which case cross-module references fold to "unresolvable".
40
+ */
41
+ export interface ImportedBindingResolver {
42
+ resolve(name: string, referenceNode: AstNode, fromScope: FoldScope): Maybe<ResolvedBinding>;
43
+ }
44
+ /**
45
+ * A binding resolved to its declaration node plus the scope it belongs to.
46
+ */
47
+ export interface ResolvedBinding {
48
+ readonly node: AstNode;
49
+ readonly scope: FoldScope;
50
+ }
51
+ /**
52
+ * The lexical scope a node is folded in: the Program it belongs to and the import registry
53
+ * for that module. Threaded through folding so an inlined cross-module function resolves its
54
+ * own identifiers against its own module.
55
+ */
56
+ export interface FoldScope {
57
+ readonly program: AstNode;
58
+ readonly importRegistry: ImportRegistry;
59
+ readonly resolver: Maybe<ImportedBindingResolver>;
60
+ }
61
+ /**
62
+ * Statically folds a `StorageFilePurposeUploadPolicy.buildUploadPath` builder to an abstract
63
+ * path template. The builder is an arrow `(input) => <path expression>` whose destructured /
64
+ * positional params become wildcards and whose body is a composition of string literals,
65
+ * `const`s, `@dereekb/util` path combinators, and foldable single-`return` helper functions.
66
+ *
67
+ * @param builderNode - The `buildUploadPath` value node (arrow / function expression, possibly behind a type assertion).
68
+ * @param scope - The lexical scope the builder lives in.
69
+ * @returns The folded path, or a reason the builder is unresolvable.
70
+ */
71
+ export declare function foldUploadPath(builderNode: AstNode, scope: FoldScope): FoldUploadPathResult;
72
+ /**
73
+ * Folds an expression to a concrete string literal value, reusing the same fragment folder the
74
+ * path evaluator uses (literals, template literals, `const`s — local + cross-module — `+`
75
+ * concatenation, modeled combinators, inlinable helpers). Returns null when the expression cannot
76
+ * be reduced to a wildcard-free constant string, so callers preserve the sound "never guess"
77
+ * contract.
78
+ *
79
+ * @param node - The expression node.
80
+ * @param scope - The lexical scope the expression lives in.
81
+ * @returns The folded string, or null when unresolvable.
82
+ */
83
+ export declare function foldStringExpression(node: AstNode, scope: FoldScope): Maybe<string>;
84
+ /**
85
+ * Folds an expression to a list of concrete strings: an inline array literal (each element folded
86
+ * via {@link foldStringExpression}, spreads of statically-known lists expanded), or an identifier
87
+ * resolving to such an array const (local or cross-module). Returns null when any element is
88
+ * unfoldable, so a genuinely dynamic element (e.g. a function call) still surfaces as unresolvable.
89
+ *
90
+ * @param node - The expression node (e.g. an `allowedMimeTypes` value).
91
+ * @param scope - The lexical scope the expression lives in.
92
+ * @returns The folded string list, or null when unresolvable.
93
+ */
94
+ export declare function foldStringArrayExpression(node: AstNode, scope: FoldScope): Maybe<readonly string[]>;
95
+ /**
96
+ * Folds a numeric expression to a number: literals, unary `-`/`+`, binary `*`/`+`/`-`/`/`,
97
+ * type-assertion see-through, and identifiers resolving to a numeric `const` (local or
98
+ * cross-module, read from the AST initializer). Returns null when any operand is unresolvable.
99
+ *
100
+ * @param node - The expression node (e.g. a `maxFileSizeBytes` value).
101
+ * @param scope - The lexical scope the expression lives in.
102
+ * @returns The folded number, or null when unresolvable.
103
+ */
104
+ export declare function foldNumericExpression(node: AstNode, scope: FoldScope): Maybe<number>;
105
+ /**
106
+ * Structurally compares a folded upload path against a Firebase rules match-path segment list
107
+ * (`{var}` / `{var=**}` → wildcard, otherwise literal). Literal segments must match value;
108
+ * wildcard segments compare equal to one another.
109
+ *
110
+ * @param folded - The folded upload path.
111
+ * @param ruleSegments - The rules match-path segments.
112
+ * @returns True when the two segment lists match position-for-position.
113
+ */
114
+ export declare function foldedPathMatchesRuleSegments(folded: FoldedUploadPath, ruleSegments: readonly FoldedPathSegment[]): boolean;
115
+ /**
116
+ * Renders a folded path for diagnostics, e.g. `uploads/u/{*}/jr/{*}`.
117
+ *
118
+ * @param folded - The folded path.
119
+ * @returns The display string.
120
+ */
121
+ export declare function describeFoldedPath(folded: FoldedUploadPath): string;
@@ -3,6 +3,50 @@ import type { Maybe } from '@dereekb/util';
3
3
  * Module that publishes the `@dereekb/firebase` Firestore constraint factories (`where`, `orderBy`, etc.).
4
4
  */
5
5
  export declare const FIREBASE_MODULE = "@dereekb/firebase";
6
+ /**
7
+ * Directory names the layout-agnostic discovery globs never descend into: installed dependencies
8
+ * and build/cache output. Excluding these keeps a broad `**\/*.ts` scan from walking a downstream
9
+ * consumer's `node_modules` (which can hold tens of thousands of declaration files) and from
10
+ * double-counting compiled output under `dist`.
11
+ */
12
+ export declare const DEFAULT_DISCOVERY_EXCLUDED_DIRS: readonly string[];
13
+ /**
14
+ * Builds the `exclude` predicate passed to `fs.globSync(pattern, { cwd, exclude })` for the broad,
15
+ * layout-agnostic discovery globs. Node invokes the predicate on each visited path (directories
16
+ * included) and prunes the subtree when it returns true, so excluding a directory name here stops
17
+ * the walk from ever descending into it.
18
+ *
19
+ * @param excludedDirs - Directory names to prune. Defaults to {@link DEFAULT_DISCOVERY_EXCLUDED_DIRS}.
20
+ * @returns A predicate that returns true for any path containing an excluded directory segment.
21
+ */
22
+ export declare function discoveryGlobExcludeFilter(excludedDirs?: readonly string[]): (path: string) => boolean;
23
+ /**
24
+ * Resolves the absolute path to the installed `@dereekb/firebase` package's `src/lib/model`
25
+ * directory, as seen from the ESLint `cwd`. Used by rules that need to read the framework model
26
+ * declarations (identities, service factories) directly from the package a consumer has installed —
27
+ * the downstream case where these live under `node_modules/@dereekb/firebase/...` as compiled
28
+ * bundles plus `.d.ts` rather than a scannable source tree.
29
+ *
30
+ * Resolution uses Node's own module resolver (`require.resolve('@dereekb/firebase/package.json')`
31
+ * anchored at `cwd`), so it transparently handles hoisting / nested `node_modules`. Returns null
32
+ * when `@dereekb/firebase` is not resolvable as a dependency from `cwd` (e.g. inside the
33
+ * dbx-components monorepo itself, where it is consumed via TS path mapping rather than
34
+ * `node_modules`) — callers fall back to their cwd-relative source globs in that case.
35
+ *
36
+ * @param cwd - The ESLint working directory to resolve the dependency from.
37
+ * @returns The absolute model directory, or null when it cannot be resolved.
38
+ */
39
+ export declare function resolveInstalledFirebaseModelDir(cwd: string): Maybe<string>;
40
+ /**
41
+ * Resolves the referenced type name from either a `TSTypeReference` (`Foo<…>` / `ns.Foo<…>`) or a
42
+ * `TSImportType` (`import("…").Foo<…>` — the form the TypeScript compiler emits in declaration
43
+ * files for cross-module type references). Returns the rightmost identifier name in a qualified
44
+ * name.
45
+ *
46
+ * @param node - A `TSTypeReference` or `TSImportType` node (or anything else).
47
+ * @returns The referenced type name, or null when the node is neither shape.
48
+ */
49
+ export declare function referencedTypeName(node: AstNode): Maybe<string>;
6
50
  /**
7
51
  * JSDoc tag name that marks an exported query factory whose body should be scanned by
8
52
  * `dbx-components-mcp`'s index extractor (`packages/dbx-components-mcp/src/scan/model-firebase-index-extract.ts`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/firebase",
3
- "version": "13.12.1",
3
+ "version": "13.12.2",
4
4
  "sideEffects": false,
5
5
  "exports": {
6
6
  "./test": {
@@ -24,10 +24,10 @@
24
24
  }
25
25
  },
26
26
  "peerDependencies": {
27
- "@dereekb/util": "13.12.1",
28
- "@dereekb/date": "13.12.1",
29
- "@dereekb/model": "13.12.1",
30
- "@dereekb/rxjs": "13.12.1",
27
+ "@dereekb/util": "13.12.2",
28
+ "@dereekb/date": "13.12.2",
29
+ "@dereekb/model": "13.12.2",
30
+ "@dereekb/rxjs": "13.12.2",
31
31
  "@firebase/rules-unit-testing": "5.0.0",
32
32
  "arktype": "^2.2.0",
33
33
  "date-fns": "^4.1.0",
package/test/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dereekb/firebase/test",
3
- "version": "13.12.1",
3
+ "version": "13.12.2",
4
4
  "peerDependencies": {
5
- "@dereekb/date": "13.12.1",
6
- "@dereekb/firebase": "13.12.1",
7
- "@dereekb/model": "13.12.1",
8
- "@dereekb/rxjs": "13.12.1",
9
- "@dereekb/util": "13.12.1",
5
+ "@dereekb/date": "13.12.2",
6
+ "@dereekb/firebase": "13.12.2",
7
+ "@dereekb/model": "13.12.2",
8
+ "@dereekb/rxjs": "13.12.2",
9
+ "@dereekb/util": "13.12.2",
10
10
  "@firebase/rules-unit-testing": "5.0.0",
11
11
  "date-fns": "^4.1.0",
12
12
  "firebase": "^12.12.1",