nexabase-console 2.1.1 → 2.1.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.
Files changed (35) hide show
  1. package/dist/app/NexaApp.d.ts +3 -0
  2. package/dist/app/NexaApp.js +40 -11
  3. package/dist/firestore/CollectionReference.d.ts +2 -2
  4. package/dist/firestore/CollectionReference.js +41 -2
  5. package/dist/firestore/DocumentReference.d.ts +1 -1
  6. package/dist/firestore/DocumentReference.js +41 -11
  7. package/dist/firestore/FieldPath.d.ts +2 -1
  8. package/dist/firestore/FieldPath.js +31 -4
  9. package/dist/firestore/FieldValue.d.ts +8 -4
  10. package/dist/firestore/FieldValue.js +59 -7
  11. package/dist/firestore/Firestore.js +16 -4
  12. package/dist/firestore/GeoPoint.d.ts +1 -0
  13. package/dist/firestore/GeoPoint.js +13 -6
  14. package/dist/firestore/Query.d.ts +5 -0
  15. package/dist/firestore/Query.js +190 -56
  16. package/dist/firestore/QueryUtils.d.ts +19 -2
  17. package/dist/firestore/QueryUtils.js +296 -58
  18. package/dist/firestore/Snapshot.js +62 -17
  19. package/dist/firestore/SnapshotManager.js +27 -12
  20. package/dist/firestore/Timestamp.d.ts +1 -0
  21. package/dist/firestore/Timestamp.js +28 -2
  22. package/dist/firestore/batch.js +89 -36
  23. package/dist/firestore/pathValidation.d.ts +17 -0
  24. package/dist/firestore/pathValidation.js +61 -0
  25. package/dist/firestore/queryConstraints.d.ts +4 -4
  26. package/dist/firestore/queryConstraints.js +69 -8
  27. package/dist/firestore/transaction.js +72 -18
  28. package/dist/firestore/writes.d.ts +1 -1
  29. package/dist/firestore/writes.js +32 -14
  30. package/dist/transport/WebSocketClient.d.ts +341 -9
  31. package/dist/transport/WebSocketClient.js +1895 -131
  32. package/dist/transport/WebSocketClient.test.d.ts +1 -0
  33. package/dist/transport/WebSocketClient.test.js +454 -0
  34. package/dist/types/index.d.ts +1 -0
  35. package/package.json +1 -1
@@ -32,6 +32,8 @@ export declare class NexaApp {
32
32
  _firestoreListening: boolean;
33
33
  _firestoreUnsubscribes: (() => void)[];
34
34
  _initPromise: Promise<void> | null;
35
+ _initialized: boolean;
36
+ _activeQueryTimestamps: Map<string, number>;
35
37
  constructor(config: NexaConfig);
36
38
  get _firestoreCache(): Record<string, any>;
37
39
  private _setupCrossTabSync;
@@ -85,6 +87,7 @@ export declare class NexaApp {
85
87
  revision?: string;
86
88
  }): Promise<void>;
87
89
  _syncOfflineQueue(): Promise<void>;
90
+ _handleServerFirestoreChange(payload: any): void;
88
91
  _ensureFirestoreSSE(): void;
89
92
  _ensureFirestoreWebSocket(): void;
90
93
  _closeFirestoreSSEIfIdle(): void;
@@ -22,6 +22,8 @@ class NexaApp {
22
22
  this._firestoreListening = false;
23
23
  this._firestoreUnsubscribes = [];
24
24
  this._initPromise = null;
25
+ this._initialized = false;
26
+ this._activeQueryTimestamps = new Map();
25
27
  this.projectId = config.projectId;
26
28
  this.token = config.apiKey || null;
27
29
  this.enablePersistence = config.enablePersistence !== false;
@@ -82,8 +84,10 @@ class NexaApp {
82
84
  this.mutationQueue.unmarkInflight(path, mutationId);
83
85
  }
84
86
  async _initFirestoreState() {
85
- if (!this.enablePersistence)
87
+ if (!this.enablePersistence) {
88
+ this._initialized = true;
86
89
  return;
90
+ }
87
91
  try {
88
92
  await this.indexedDB.init();
89
93
  await this.localStore.loadAll();
@@ -97,6 +101,9 @@ class NexaApp {
97
101
  catch (err) {
98
102
  console.warn('[NexaApp] Failed to initialize IndexedDB offline cache:', err);
99
103
  }
104
+ finally {
105
+ this._initialized = true;
106
+ }
100
107
  }
101
108
  async _setCache(path, data) {
102
109
  const revived = (0, QueryUtils_1.reviveNexaData)(data);
@@ -348,23 +355,43 @@ class NexaApp {
348
355
  async _syncOfflineQueue() {
349
356
  await this.syncEngine.syncOfflineMutations();
350
357
  }
358
+ _handleServerFirestoreChange(payload) {
359
+ if (!payload)
360
+ return;
361
+ if (Array.isArray(payload)) {
362
+ payload.forEach((item) => this._handleServerFirestoreChange(item));
363
+ return;
364
+ }
365
+ const { type, docPath, collectionPath, data, path: rawPath, action } = payload;
366
+ const targetDocPath = docPath || rawPath;
367
+ const isDelete = type === 'document_deleted' || action === 'delete' || type === 'delete';
368
+ if (targetDocPath && typeof targetDocPath === 'string') {
369
+ if (isDelete) {
370
+ this._deleteCache(targetDocPath);
371
+ }
372
+ else if (data) {
373
+ this._setCache(targetDocPath, data);
374
+ }
375
+ const parts = targetDocPath.split('/');
376
+ const colPath = parts.length > 1 ? parts.slice(0, -1).join('/') : '';
377
+ this._notifySnapshotCallbacks(targetDocPath, isDelete ? 'document_deleted' : 'document_written', data);
378
+ if (colPath) {
379
+ this._notifySnapshotCallbacks(colPath, isDelete ? 'document_deleted' : 'document_written', data);
380
+ }
381
+ }
382
+ else if (collectionPath && typeof collectionPath === 'string') {
383
+ this._notifySnapshotCallbacks(collectionPath, 'server_update', data);
384
+ }
385
+ }
351
386
  _ensureFirestoreSSE() {
352
387
  if (this._firestoreListening)
353
388
  return;
354
389
  this._firestoreListening = true;
355
390
  const unsub1 = this.wsClient.addListener('data_changed', (payload) => {
356
- if (payload && payload.path) {
357
- if (payload.action === 'delete') {
358
- this._deleteCache(payload.path);
359
- }
360
- else if (payload.data) {
361
- this._setCache(payload.path, payload.data);
362
- }
363
- this._notifySnapshotCallbacks(payload.path, payload.action === 'delete' ? 'document_deleted' : 'document_written', payload.data);
364
- }
391
+ this._handleServerFirestoreChange(payload);
365
392
  });
366
393
  const unsub2 = this.wsClient.addListener('firestore_changed', (payload) => {
367
- this._notifySnapshotCallbacks(payload.collectionPath || payload.docPath, 'server_update', payload.data);
394
+ this._handleServerFirestoreChange(payload);
368
395
  });
369
396
  this._firestoreUnsubscribes.push(unsub1, unsub2);
370
397
  }
@@ -382,6 +409,8 @@ class NexaApp {
382
409
  this._closeFirestoreSSEIfIdle();
383
410
  }
384
411
  _notifySnapshotCallbacks(path, actionType, payload) {
412
+ if (!path || typeof path !== 'string')
413
+ return;
385
414
  Object.keys(this._snapshotCallbacks).forEach((cbKey) => {
386
415
  const parts = cbKey.split('_');
387
416
  const targetPath = parts.slice(2).join('_');
@@ -1,3 +1,3 @@
1
1
  import { NexaApp } from '../app/NexaApp';
2
- import { CollectionReference } from '../types/index';
3
- export declare const collection: <T = any>(db: NexaApp, collectionPath: string) => CollectionReference<T>;
2
+ import { CollectionReference, DocumentReference } from '../types/index';
3
+ export declare const collection: <T = any>(dbOrRef: NexaApp | DocumentReference<any> | CollectionReference<any>, ...pathSegments: string[]) => CollectionReference<T>;
@@ -1,7 +1,46 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.collection = void 0;
4
- const collection = (db, collectionPath) => {
5
- return { type: 'collection', path: collectionPath, db };
4
+ const NexaError_1 = require("../errors/NexaError");
5
+ const pathValidation_1 = require("./pathValidation");
6
+ const collection = (dbOrRef, ...pathSegments) => {
7
+ if (!dbOrRef || typeof dbOrRef !== 'object') {
8
+ throw new NexaError_1.NexaError('invalid-argument', 'First argument to collection() must be a valid NexaApp, DocumentReference, or CollectionReference.');
9
+ }
10
+ if ('type' in dbOrRef && dbOrRef.type === 'document') {
11
+ if (pathSegments.length === 0) {
12
+ throw new NexaError_1.NexaError('invalid-argument', 'collection() called with DocumentReference requires path segments.');
13
+ }
14
+ const joined = pathSegments.join('/');
15
+ const finalPath = (0, pathValidation_1.validateCollectionPath)(`${dbOrRef.path}/${joined}`);
16
+ return {
17
+ type: 'collection',
18
+ path: finalPath,
19
+ db: dbOrRef.db
20
+ };
21
+ }
22
+ if ('type' in dbOrRef && dbOrRef.type === 'collection') {
23
+ if (pathSegments.length === 0) {
24
+ return dbOrRef;
25
+ }
26
+ const joined = pathSegments.join('/');
27
+ const finalPath = (0, pathValidation_1.validateCollectionPath)(`${dbOrRef.path}/${joined}`);
28
+ return {
29
+ type: 'collection',
30
+ path: finalPath,
31
+ db: dbOrRef.db
32
+ };
33
+ }
34
+ // dbOrRef is NexaApp
35
+ if (pathSegments.length === 0) {
36
+ throw new NexaError_1.NexaError('invalid-argument', 'collection() called with database reference requires a path.');
37
+ }
38
+ const joined = pathSegments.join('/');
39
+ const finalPath = (0, pathValidation_1.validateCollectionPath)(joined);
40
+ return {
41
+ type: 'collection',
42
+ path: finalPath,
43
+ db: dbOrRef
44
+ };
6
45
  };
7
46
  exports.collection = collection;
@@ -1,3 +1,3 @@
1
1
  import { NexaApp } from '../app/NexaApp';
2
2
  import { CollectionReference, DocumentReference } from '../types/index';
3
- export declare const doc: <T = any>(dbOrCollection: NexaApp | CollectionReference<any>, ...pathSegments: string[]) => DocumentReference<T>;
3
+ export declare const doc: <T = any>(dbOrRef: NexaApp | CollectionReference<any> | DocumentReference<any>, ...pathSegments: string[]) => DocumentReference<T>;
@@ -1,22 +1,52 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.doc = void 0;
4
- const doc = (dbOrCollection, ...pathSegments) => {
5
- let docRef;
6
- if ('type' in dbOrCollection && dbOrCollection.type === 'collection') {
7
- docRef = {
4
+ const NexaError_1 = require("../errors/NexaError");
5
+ const pathValidation_1 = require("./pathValidation");
6
+ const doc = (dbOrRef, ...pathSegments) => {
7
+ if (!dbOrRef || typeof dbOrRef !== 'object') {
8
+ throw new NexaError_1.NexaError('invalid-argument', 'First argument to doc() must be a valid NexaApp, CollectionReference, or DocumentReference.');
9
+ }
10
+ if ('type' in dbOrRef && dbOrRef.type === 'collection') {
11
+ if (pathSegments.length === 0) {
12
+ const autoId = (0, pathValidation_1.generateAutoId)();
13
+ const finalPath = (0, pathValidation_1.validateDocumentPath)(`${dbOrRef.path}/${autoId}`);
14
+ return {
15
+ type: 'document',
16
+ path: finalPath,
17
+ db: dbOrRef.db
18
+ };
19
+ }
20
+ const joined = pathSegments.join('/');
21
+ const finalPath = (0, pathValidation_1.validateDocumentPath)(`${dbOrRef.path}/${joined}`);
22
+ return {
8
23
  type: 'document',
9
- path: `${dbOrCollection.path}/${pathSegments.join('/')}`,
10
- db: dbOrCollection.db
24
+ path: finalPath,
25
+ db: dbOrRef.db
11
26
  };
12
27
  }
13
- else {
14
- docRef = {
28
+ if ('type' in dbOrRef && dbOrRef.type === 'document') {
29
+ if (pathSegments.length === 0) {
30
+ return dbOrRef;
31
+ }
32
+ const joined = pathSegments.join('/');
33
+ const finalPath = (0, pathValidation_1.validateDocumentPath)(`${dbOrRef.path}/${joined}`);
34
+ return {
15
35
  type: 'document',
16
- path: pathSegments.join('/'),
17
- db: dbOrCollection
36
+ path: finalPath,
37
+ db: dbOrRef.db
18
38
  };
19
39
  }
20
- return docRef;
40
+ // dbOrRef is NexaApp instance
41
+ if (pathSegments.length === 0) {
42
+ throw new NexaError_1.NexaError('invalid-argument', 'doc() called with database reference requires at least one path segment.');
43
+ }
44
+ const joined = pathSegments.join('/');
45
+ const finalPath = (0, pathValidation_1.validateDocumentPath)(joined);
46
+ return {
47
+ type: 'document',
48
+ path: finalPath,
49
+ db: dbOrRef
50
+ };
21
51
  };
22
52
  exports.doc = doc;
@@ -1,7 +1,8 @@
1
1
  export declare class FieldPath {
2
- private readonly segments;
2
+ readonly segments: readonly string[];
3
3
  constructor(...fieldNames: string[]);
4
4
  static documentId(): FieldPath;
5
5
  getPathString(): string;
6
6
  isEqual(other: FieldPath): boolean;
7
+ toString(): string;
7
8
  }
@@ -1,23 +1,50 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FieldPath = void 0;
4
+ const NexaError_1 = require("../errors/NexaError");
5
+ const CONTROL_CHARS_REGEX = /[\x00-\x1F\x7F]/;
6
+ const SIMPLE_FIELD_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
4
7
  class FieldPath {
5
8
  constructor(...fieldNames) {
6
- if (fieldNames.length === 0) {
7
- throw new Error('FieldPath must contain at least one field name');
9
+ if (!fieldNames || fieldNames.length === 0) {
10
+ throw new NexaError_1.NexaError('invalid-argument', 'FieldPath must contain at least one field name.');
8
11
  }
9
- this.segments = fieldNames;
12
+ for (let i = 0; i < fieldNames.length; i++) {
13
+ const name = fieldNames[i];
14
+ if (typeof name !== 'string' || name.length === 0) {
15
+ throw new NexaError_1.NexaError('invalid-argument', `Invalid field path segment at index ${i}. Provided field name must be a non-empty string.`);
16
+ }
17
+ if (CONTROL_CHARS_REGEX.test(name)) {
18
+ throw new NexaError_1.NexaError('invalid-argument', `FieldPath segment "${name}" contains invalid control characters.`);
19
+ }
20
+ }
21
+ this.segments = Object.freeze([...fieldNames]);
22
+ Object.freeze(this);
10
23
  }
11
24
  static documentId() {
12
25
  return new FieldPath('__name__');
13
26
  }
14
27
  getPathString() {
15
- return this.segments.join('.');
28
+ return this.segments
29
+ .map((seg) => {
30
+ if (SIMPLE_FIELD_NAME_REGEX.test(seg)) {
31
+ return seg;
32
+ }
33
+ // Escape backslashes and backticks, then wrap in backticks
34
+ const escaped = seg.replace(/\\/g, '\\\\').replace(/`/g, '\\`');
35
+ return `\`${escaped}\``;
36
+ })
37
+ .join('.');
16
38
  }
17
39
  isEqual(other) {
40
+ if (!(other instanceof FieldPath))
41
+ return false;
18
42
  if (this.segments.length !== other.segments.length)
19
43
  return false;
20
44
  return this.segments.every((seg, idx) => seg === other.segments[idx]);
21
45
  }
46
+ toString() {
47
+ return this.getPathString();
48
+ }
22
49
  }
23
50
  exports.FieldPath = FieldPath;
@@ -1,8 +1,12 @@
1
+ export declare const FIELD_VALUE_BRAND: unique symbol;
2
+ export type FieldValueMethod = 'serverTimestamp' | 'increment' | 'arrayUnion' | 'arrayRemove' | 'deleteField';
1
3
  export declare class FieldValue {
2
- method: string;
3
- value?: any | undefined;
4
- _isFieldValue: boolean;
5
- constructor(method: string, value?: any | undefined);
4
+ readonly [FIELD_VALUE_BRAND] = true;
5
+ readonly method: FieldValueMethod;
6
+ readonly value?: any;
7
+ constructor(method: FieldValueMethod, value?: any);
8
+ static isFieldValue(val: any): val is FieldValue;
9
+ isEqual(other: any): boolean;
6
10
  }
7
11
  export declare const serverTimestamp: () => FieldValue;
8
12
  export declare const increment: (n: number) => FieldValue;
@@ -1,26 +1,78 @@
1
1
  "use strict";
2
+ var _a;
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.atomicDecrement = exports.atomicIncrement = exports.deleteField = exports.arrayRemove = exports.arrayUnion = exports.increment = exports.serverTimestamp = exports.FieldValue = void 0;
4
+ exports.atomicDecrement = exports.atomicIncrement = exports.deleteField = exports.arrayRemove = exports.arrayUnion = exports.increment = exports.serverTimestamp = exports.FieldValue = exports.FIELD_VALUE_BRAND = void 0;
5
+ const NexaError_1 = require("../errors/NexaError");
6
+ exports.FIELD_VALUE_BRAND = Symbol.for('nexabase.fieldValue');
4
7
  class FieldValue {
5
8
  constructor(method, value) {
9
+ this[_a] = true;
6
10
  this.method = method;
7
11
  this.value = value;
8
- this._isFieldValue = true;
12
+ Object.freeze(this);
13
+ }
14
+ static isFieldValue(val) {
15
+ return (val !== null &&
16
+ typeof val === 'object' &&
17
+ (val instanceof FieldValue || val[exports.FIELD_VALUE_BRAND] === true));
18
+ }
19
+ isEqual(other) {
20
+ if (!FieldValue.isFieldValue(other))
21
+ return false;
22
+ if (this.method !== other.method)
23
+ return false;
24
+ if (this.value === other.value)
25
+ return true;
26
+ if (Array.isArray(this.value) && Array.isArray(other.value)) {
27
+ return (this.value.length === other.value.length &&
28
+ this.value.every((v, i) => v === other.value[i]));
29
+ }
30
+ return false;
9
31
  }
10
32
  }
11
33
  exports.FieldValue = FieldValue;
34
+ _a = exports.FIELD_VALUE_BRAND;
12
35
  const serverTimestamp = () => new FieldValue('serverTimestamp');
13
36
  exports.serverTimestamp = serverTimestamp;
14
- const increment = (n) => new FieldValue('increment', n);
37
+ const increment = (n) => {
38
+ if (typeof n !== 'number' || !Number.isFinite(n)) {
39
+ throw new NexaError_1.NexaError('invalid-argument', 'FieldValue.increment() value must be a finite number.');
40
+ }
41
+ return new FieldValue('increment', n);
42
+ };
15
43
  exports.increment = increment;
16
- const arrayUnion = (...elements) => new FieldValue('arrayUnion', elements);
44
+ const arrayUnion = (...elements) => {
45
+ for (let i = 0; i < elements.length; i++) {
46
+ if (FieldValue.isFieldValue(elements[i])) {
47
+ throw new NexaError_1.NexaError('invalid-argument', 'FieldValue.arrayUnion() elements cannot contain nested FieldValues.');
48
+ }
49
+ }
50
+ return new FieldValue('arrayUnion', Object.freeze([...elements]));
51
+ };
17
52
  exports.arrayUnion = arrayUnion;
18
- const arrayRemove = (...elements) => new FieldValue('arrayRemove', elements);
53
+ const arrayRemove = (...elements) => {
54
+ for (let i = 0; i < elements.length; i++) {
55
+ if (FieldValue.isFieldValue(elements[i])) {
56
+ throw new NexaError_1.NexaError('invalid-argument', 'FieldValue.arrayRemove() elements cannot contain nested FieldValues.');
57
+ }
58
+ }
59
+ return new FieldValue('arrayRemove', Object.freeze([...elements]));
60
+ };
19
61
  exports.arrayRemove = arrayRemove;
20
62
  const deleteField = () => new FieldValue('deleteField');
21
63
  exports.deleteField = deleteField;
22
64
  // Helper methods for Offline-First operations (e.g. POS/Inventory systems)
23
- const atomicIncrement = (n) => new FieldValue('increment', Math.abs(n));
65
+ const atomicIncrement = (n) => {
66
+ if (typeof n !== 'number' || !Number.isFinite(n)) {
67
+ throw new NexaError_1.NexaError('invalid-argument', 'atomicIncrement() value must be a finite number.');
68
+ }
69
+ return new FieldValue('increment', Math.abs(n));
70
+ };
24
71
  exports.atomicIncrement = atomicIncrement;
25
- const atomicDecrement = (n) => new FieldValue('increment', -Math.abs(n));
72
+ const atomicDecrement = (n) => {
73
+ if (typeof n !== 'number' || !Number.isFinite(n)) {
74
+ throw new NexaError_1.NexaError('invalid-argument', 'atomicDecrement() value must be a finite number.');
75
+ }
76
+ return new FieldValue('increment', -Math.abs(n));
77
+ };
26
78
  exports.atomicDecrement = atomicDecrement;
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.enableOfflinePersistence = exports.enableIndexedDbPersistence = exports.Firestore = void 0;
4
+ const NexaError_1 = require("../errors/NexaError");
4
5
  class Firestore {
5
6
  constructor(app) {
7
+ if (!app) {
8
+ throw new NexaError_1.NexaError('invalid-argument', 'Firestore constructor requires a valid NexaApp instance.');
9
+ }
6
10
  this.app = app;
7
11
  }
8
12
  getApp() {
@@ -11,14 +15,22 @@ class Firestore {
11
15
  }
12
16
  exports.Firestore = Firestore;
13
17
  const enableIndexedDbPersistence = async (db) => {
18
+ if (!db) {
19
+ throw new NexaError_1.NexaError('invalid-argument', 'enableIndexedDbPersistence requires a valid NexaApp database instance.');
20
+ }
21
+ if (typeof window !== 'undefined' && typeof window.indexedDB === 'undefined' && typeof indexedDB === 'undefined') {
22
+ throw new NexaError_1.NexaError('unimplemented', 'IndexedDB is not supported in the current runtime environment.');
23
+ }
24
+ if (db._initialized && !db.enablePersistence) {
25
+ throw new NexaError_1.NexaError('failed-precondition', 'Firestore persistence must be enabled before any other Firestore operations are executed.');
26
+ }
14
27
  db.enablePersistence = true;
15
- if (db._initPromise)
28
+ if (db._initPromise) {
16
29
  await db._initPromise;
30
+ }
17
31
  };
18
32
  exports.enableIndexedDbPersistence = enableIndexedDbPersistence;
19
33
  const enableOfflinePersistence = async (db) => {
20
- db.enablePersistence = true;
21
- if (db._initPromise)
22
- await db._initPromise;
34
+ return (0, exports.enableIndexedDbPersistence)(db);
23
35
  };
24
36
  exports.enableOfflinePersistence = enableOfflinePersistence;
@@ -3,6 +3,7 @@ export declare class GeoPoint {
3
3
  readonly longitude: number;
4
4
  constructor(latitude: number, longitude: number);
5
5
  isEqual(other: GeoPoint): boolean;
6
+ toString(): string;
6
7
  toJSON(): {
7
8
  latitude: number;
8
9
  longitude: number;
@@ -1,20 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GeoPoint = void 0;
4
+ const NexaError_1 = require("../errors/NexaError");
4
5
  class GeoPoint {
5
6
  constructor(latitude, longitude) {
6
- this.latitude = latitude;
7
- this.longitude = longitude;
8
- if (isNaN(latitude) || latitude < -90 || latitude > 90) {
9
- throw new Error(`Latitude must be a number between -90 and 90, but got ${latitude}`);
7
+ if (typeof latitude !== 'number' || !Number.isFinite(latitude) || latitude < -90 || latitude > 90) {
8
+ throw new NexaError_1.NexaError('invalid-argument', `Latitude must be a finite number between -90 and 90, but got ${latitude}`);
10
9
  }
11
- if (isNaN(longitude) || longitude < -180 || longitude > 180) {
12
- throw new Error(`Longitude must be a number between -180 and 180, but got ${longitude}`);
10
+ if (typeof longitude !== 'number' || !Number.isFinite(longitude) || longitude < -180 || longitude > 180) {
11
+ throw new NexaError_1.NexaError('invalid-argument', `Longitude must be a finite number between -180 and 180, but got ${longitude}`);
13
12
  }
13
+ this.latitude = latitude;
14
+ this.longitude = longitude;
15
+ Object.freeze(this);
14
16
  }
15
17
  isEqual(other) {
18
+ if (!(other instanceof GeoPoint))
19
+ return false;
16
20
  return this.latitude === other.latitude && this.longitude === other.longitude;
17
21
  }
22
+ toString() {
23
+ return `GeoPoint(latitude=${this.latitude}, longitude=${this.longitude})`;
24
+ }
18
25
  toJSON() {
19
26
  return { latitude: this.latitude, longitude: this.longitude };
20
27
  }
@@ -1,5 +1,10 @@
1
1
  import { FieldPath } from './FieldPath';
2
2
  import { CollectionReference, Query, QueryConstraint, QuerySnapshot, AggregateField, AggregateSpec, AggregateSpecData, AggregateQuerySnapshot } from '../types/index';
3
+ export declare function getCanonicalQueryKey(collectionPath: string, constraints: QueryConstraint[]): string;
4
+ export declare function validateAndDeduplicateServerDocs(docs: any[]): Array<{
5
+ id: string;
6
+ fields: any;
7
+ }>;
3
8
  export declare const query: <T = any>(queryObject: Query<T> | CollectionReference<T>, ...queryConstraints: QueryConstraint[]) => Query<T>;
4
9
  export declare const getCachedDocs: <T = any>(queryOrCollection: Query<T> | CollectionReference<T>, fromCache?: boolean) => QuerySnapshot<T>;
5
10
  export declare const getDocs: <T = any>(queryOrCollection: Query<T> | CollectionReference<T>) => Promise<QuerySnapshot<T>>;