@cougargrades/firebase-rest-firestore 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Utility class for constructing Firestore URIs
3
+ * Consistently handles different types of paths and operations
4
+ */
5
+ export class FirestorePath {
6
+ /**
7
+ * Constructor
8
+ */
9
+ constructor(config, debug = false) {
10
+ this.useEmulator = false;
11
+ this.emulatorHost = "localhost";
12
+ this.emulatorPort = 8080;
13
+ this.debug = false;
14
+ this.projectId = config.projectId;
15
+ this.databaseId = config.databaseId || "(default)";
16
+ this.debug = debug;
17
+ if (config.useEmulator) {
18
+ this.useEmulator = true;
19
+ this.emulatorHost = config.emulatorHost || "localhost";
20
+ this.emulatorPort = config.emulatorPort || 8080;
21
+ }
22
+ }
23
+ /**
24
+ * Get Firestore base URL (without document path)
25
+ */
26
+ getBasePath() {
27
+ const baseUrl = this.useEmulator
28
+ ? `http://${this.emulatorHost}:${this.emulatorPort}/v1`
29
+ : "https://firestore.googleapis.com/v1";
30
+ const path = `${baseUrl}/projects/${this.projectId}/databases/${this.databaseId}/documents`;
31
+ if (this.debug) {
32
+ console.log(`Generated base path: ${path}`);
33
+ }
34
+ return path;
35
+ }
36
+ /**
37
+ * Get base URL + collection path for a collection root
38
+ * @param path Collection path (ex: "users" or "users/uid/posts")
39
+ */
40
+ getCollectionPath(path) {
41
+ // Remove leading and trailing slashes
42
+ const cleanPath = path.replace(/^\/+|\/+$/g, '');
43
+ const fullPath = `${this.getBasePath()}/${cleanPath}`;
44
+ if (this.debug) {
45
+ console.log(`Generated collection path: ${fullPath}`);
46
+ }
47
+ return fullPath;
48
+ }
49
+ /**
50
+ * Get the complete URL for a document
51
+ * @param collectionPath Collection path
52
+ * @param documentId Document ID
53
+ */
54
+ getDocumentPath(collectionPath, documentId) {
55
+ const cleanCollectionPath = collectionPath.replace(/^\/+|\/+$/g, '');
56
+ const path = `${this.getBasePath()}/${cleanCollectionPath}/${documentId}`;
57
+ if (this.debug) {
58
+ console.log(`Generated document path: ${path}`);
59
+ }
60
+ return path;
61
+ }
62
+ /**
63
+ * Get URL for query execution
64
+ * @param path Collection path (ex: "users" or "users/uid/posts")
65
+ * @returns URL for query execution, collection ID, and parent path (if needed)
66
+ */
67
+ getQueryPath(path) {
68
+ // パスをセグメントに分割
69
+ const segments = path.replace(/^\/+|\/+$/g, '').split('/');
70
+ // 単一コレクションの場合
71
+ if (segments.length === 1) {
72
+ const url = `${this.getBasePath()}:runQuery`;
73
+ if (this.debug) {
74
+ console.log(`Generated query URL (single collection): ${url}`);
75
+ console.log(`Collection ID: ${segments[0]}`);
76
+ }
77
+ return {
78
+ url,
79
+ collectionId: segments[0]
80
+ };
81
+ }
82
+ // ネストしたコレクションパスの場合 (例: "users/uid/posts")
83
+ const collectionId = segments[segments.length - 1];
84
+ const parentSegments = segments.slice(0, -1);
85
+ const parentPath = parentSegments.join('/');
86
+ // ベースURLでネストしたドキュメントまでのパスを取得
87
+ const url = `${this.getBasePath()}:runQuery`;
88
+ if (this.debug) {
89
+ console.log(`Generated query URL (nested collection): ${url}`);
90
+ console.log(`Collection ID: ${collectionId}`);
91
+ console.log(`Parent path: ${parentPath}`);
92
+ }
93
+ return {
94
+ url,
95
+ collectionId,
96
+ parentPath
97
+ };
98
+ }
99
+ /**
100
+ * Get reference path for parent document (for query construction)
101
+ * @param parentPath Parent document path
102
+ */
103
+ getParentReference(parentPath) {
104
+ return `projects/${this.projectId}/databases/${this.databaseId}/documents/${parentPath}`;
105
+ }
106
+ /**
107
+ * Get URL for runQuery
108
+ * @param collectionPath Collection path
109
+ * @returns URL for executing runQuery
110
+ */
111
+ getRunQueryPath(collectionPath) {
112
+ // コレクションパス情報を取得
113
+ const { collectionId, parentPath } = this.getQueryPath(collectionPath);
114
+ // parentPathがある場合は、親ドキュメントパスを使用してURLを作成
115
+ if (parentPath) {
116
+ // getBasePathからベースURLを取得
117
+ const baseUrl = this.getBasePath().replace(/\/documents$/, '');
118
+ // 親ドキュメントパスを含むrunQueryのURL
119
+ const runQueryUrl = `${baseUrl}/documents/${parentPath}:runQuery`;
120
+ if (this.debug) {
121
+ console.log(`Generated runQuery URL for nested collection: ${runQueryUrl}`);
122
+ console.log(`Collection ID: ${collectionId}`);
123
+ }
124
+ return runQueryUrl;
125
+ }
126
+ // トップレベルコレクションの場合は、ベースパスを使用
127
+ const baseUrl = this.getBasePath();
128
+ const runQueryUrl = `${baseUrl}:runQuery`;
129
+ if (this.debug) {
130
+ console.log(`Generated runQuery URL for top-level collection: ${runQueryUrl}`);
131
+ console.log(`Collection ID: ${collectionId}`);
132
+ }
133
+ return runQueryUrl;
134
+ }
135
+ }
136
+ /**
137
+ * Create an instance of FirestorePath class
138
+ * @param config Firestore configuration
139
+ * @param debug Debug mode
140
+ */
141
+ export function createFirestorePath(config, debug = false) {
142
+ return new FirestorePath(config, debug);
143
+ }
144
+ /**
145
+ * Get Firestore base path URL (without path)
146
+ * @param projectId Project ID
147
+ * @param databaseId Database ID (defaults to default)
148
+ * @param config Firestore configuration (for emulator settings)
149
+ * @returns Firestore base path URL (without path)
150
+ */
151
+ export function getFirestoreBasePath(projectId, databaseId, config) {
152
+ // Use emulator URL for emulator mode
153
+ if (config?.useEmulator) {
154
+ const host = config.emulatorHost || "127.0.0.1";
155
+ const port = config.emulatorPort || 8080;
156
+ return `http://${host}:${port}/v1/projects/${projectId}/databases/${databaseId || "(default)"}/documents`;
157
+ }
158
+ // Use normal production environment URL
159
+ return `https://firestore.googleapis.com/v1/projects/${projectId}/databases/${databaseId || "(default)"}/documents`;
160
+ }
161
+ /**
162
+ * Extract document ID from document path
163
+ * @param path Document path
164
+ * @returns Document ID
165
+ */
166
+ export function getDocumentId(path) {
167
+ const parts = path.split("/");
168
+ return parts[parts.length - 1];
169
+ }
@@ -0,0 +1,417 @@
1
+ import { FirestoreConfig, QueryOptions } from "./types";
2
+ /**
3
+ * Firestore client class
4
+ */
5
+ export declare class FirestoreClient {
6
+ private token;
7
+ private tokenExpiry;
8
+ private config;
9
+ private configChecked;
10
+ private debug;
11
+ private pathUtil;
12
+ /**
13
+ * Constructor
14
+ * @param config Firestore configuration object
15
+ */
16
+ constructor(config: FirestoreConfig);
17
+ /**
18
+ * Check configuration parameters
19
+ * @private
20
+ */
21
+ private checkConfig;
22
+ /**
23
+ * Get authentication token (with caching)
24
+ */
25
+ private getToken;
26
+ /**
27
+ * Prepare request headers
28
+ * @param additionalHeaders Additional headers
29
+ * @returns Prepared headers object
30
+ * @private
31
+ */
32
+ private prepareHeaders;
33
+ /**
34
+ * Get collection reference
35
+ * @param path Collection path
36
+ * @returns CollectionReference instance
37
+ */
38
+ collection(path: string): CollectionReference;
39
+ /**
40
+ * Get document reference
41
+ * @param path Document path
42
+ * @returns DocumentReference instance
43
+ */
44
+ doc(path: string): DocumentReference;
45
+ /**
46
+ * Get collection group reference
47
+ * @param path Collection group ID
48
+ * @returns CollectionGroup instance
49
+ */
50
+ collectionGroup(path: string): CollectionGroup;
51
+ /**
52
+ * Add document to Firestore
53
+ * @param collectionName Collection name
54
+ * @param data Data to add
55
+ * @returns Added document
56
+ */
57
+ add(collectionName: string, data: Record<string, any>): Promise<Record<string, any> & {
58
+ id: string;
59
+ }>;
60
+ /**
61
+ * Get document
62
+ * @param collectionName Collection name
63
+ * @param documentId Document ID
64
+ * @returns Retrieved document (null if it doesn't exist)
65
+ */
66
+ get(collectionName: string, documentId: string): Promise<(Record<string, any> & {
67
+ id: string;
68
+ }) | null>;
69
+ /**
70
+ * Update document
71
+ * @param collectionName Collection name
72
+ * @param documentId Document ID
73
+ * @param data Data to update
74
+ * @returns Updated document
75
+ */
76
+ update(collectionName: string, documentId: string, data: Record<string, any>): Promise<Record<string, any> & {
77
+ id: string;
78
+ }>;
79
+ /**
80
+ * Delete document
81
+ * @param collectionName Collection name
82
+ * @param documentId Document ID
83
+ * @returns true if deletion successful
84
+ */
85
+ delete(collectionName: string, documentId: string): Promise<boolean>;
86
+ /**
87
+ * Query documents in a collection
88
+ * @param collectionPath Collection path
89
+ * @param options Query options
90
+ * @param allDescendants Whether to include descendant collections
91
+ * @returns Array of documents matching the query
92
+ */
93
+ query(collectionPath: string, options?: QueryOptions, allDescendants?: boolean): Promise<(Record<string, any> & {
94
+ id: string;
95
+ })[]>;
96
+ /**
97
+ * ドキュメントを作成または上書き
98
+ * @param collectionName コレクション名
99
+ * @param documentId ドキュメントID
100
+ * @param data ドキュメントデータ
101
+ * @returns 作成されたドキュメントのリファレンス
102
+ */
103
+ createWithId(collectionName: string, documentId: string, data: Record<string, any>): Promise<Record<string, any> & {
104
+ id: string;
105
+ }>;
106
+ }
107
+ /**
108
+ * Collection reference class
109
+ */
110
+ export declare class CollectionReference {
111
+ private client;
112
+ private _path;
113
+ private _queryConstraints;
114
+ constructor(client: FirestoreClient, path: string);
115
+ /**
116
+ * Get collection path
117
+ */
118
+ get path(): string;
119
+ /**
120
+ * Whether to include all descendant collections
121
+ */
122
+ get allDescendants(): boolean;
123
+ /**
124
+ * Get document reference
125
+ * @param documentPath Document ID (auto-generated if omitted)
126
+ * @returns DocumentReference instance
127
+ */
128
+ doc(documentPath?: string): DocumentReference;
129
+ /**
130
+ * Add document (ID is auto-generated)
131
+ * @param data Document data
132
+ * @returns Reference to the created document
133
+ */
134
+ add(data: Record<string, any>): Promise<DocumentReference>;
135
+ /**
136
+ * Add filter condition
137
+ * @param fieldPath Field path
138
+ * @param opStr Operator
139
+ * @param value Value
140
+ * @returns Query instance
141
+ */
142
+ where(fieldPath: string, opStr: string, value: any): Query;
143
+ /**
144
+ * Add sorting condition
145
+ * @param fieldPath Field path
146
+ * @param directionStr Sort direction ('asc' or 'desc')
147
+ * @returns Query instance
148
+ */
149
+ orderBy(fieldPath: string, directionStr?: "asc" | "desc"): Query;
150
+ /**
151
+ * Set limit on number of results
152
+ * @param limit Maximum number
153
+ * @returns Query instance
154
+ */
155
+ limit(limit: number): Query;
156
+ /**
157
+ * Set number of documents to skip
158
+ * @param offset Number to skip
159
+ * @returns Query instance
160
+ */
161
+ offset(offset: number): Query;
162
+ /**
163
+ * Execute query
164
+ * @returns QuerySnapshot instance
165
+ */
166
+ get(): Promise<QuerySnapshot>;
167
+ /**
168
+ * Generate random ID
169
+ * @returns Random ID
170
+ */
171
+ private _generateId;
172
+ }
173
+ /**
174
+ * Document reference class
175
+ */
176
+ export declare class DocumentReference {
177
+ private client;
178
+ private collectionPath;
179
+ private docId;
180
+ constructor(client: FirestoreClient, collectionPath: string, docId: string);
181
+ /**
182
+ * Get document ID
183
+ */
184
+ get id(): string;
185
+ /**
186
+ * Get document path
187
+ */
188
+ get path(): string;
189
+ /**
190
+ * Get parent collection reference
191
+ */
192
+ get parent(): CollectionReference;
193
+ /**
194
+ * Get subcollection
195
+ * @param collectionPath Subcollection name
196
+ * @returns CollectionReference instance
197
+ */
198
+ collection(collectionPath: string): CollectionReference;
199
+ /**
200
+ * Get document
201
+ * @returns DocumentSnapshot instance
202
+ */
203
+ get(): Promise<DocumentSnapshot>;
204
+ /**
205
+ * Create or overwrite document
206
+ * @param data Document data
207
+ * @param options Options (merge is not currently supported)
208
+ * @returns WriteResult instance
209
+ */
210
+ set(data: Record<string, any>, options?: {
211
+ merge?: boolean;
212
+ }): Promise<WriteResult>;
213
+ /**
214
+ * Update document
215
+ * @param data Update data
216
+ * @returns WriteResult instance
217
+ */
218
+ update(data: Record<string, any>): Promise<WriteResult>;
219
+ /**
220
+ * Delete document
221
+ * @returns WriteResult instance
222
+ */
223
+ delete(): Promise<WriteResult>;
224
+ }
225
+ /**
226
+ * Collection group
227
+ */
228
+ export declare class CollectionGroup {
229
+ private client;
230
+ private path;
231
+ private _queryConstraints;
232
+ constructor(client: FirestoreClient, path: string);
233
+ /**
234
+ * Whether to include all descendant collections
235
+ */
236
+ get allDescendants(): boolean;
237
+ /**
238
+ * Add filter condition
239
+ * @param fieldPath Field path
240
+ * @param opStr Operator
241
+ * @param value Value
242
+ * @returns Query instance
243
+ */
244
+ where(fieldPath: string, opStr: string, value: any): Query;
245
+ /**
246
+ * Add sorting condition
247
+ * @param fieldPath Field path
248
+ * @param directionStr Sort direction ('asc' or 'desc')
249
+ * @returns Query instance
250
+ */
251
+ orderBy(fieldPath: string, directionStr?: "asc" | "desc"): Query;
252
+ /**
253
+ * Set limit on number of results
254
+ * @param limit Maximum number
255
+ * @returns Query instance
256
+ */
257
+ limit(limit: number): Query;
258
+ /**
259
+ * Set number of documents to skip
260
+ * @param offset Number to skip
261
+ * @returns Query instance
262
+ */
263
+ offset(offset: number): Query;
264
+ /**
265
+ * Execute query
266
+ * @returns QuerySnapshot instance
267
+ */
268
+ get(): Promise<QuerySnapshot>;
269
+ }
270
+ /**
271
+ * Query class
272
+ */
273
+ export declare class Query {
274
+ private client;
275
+ private collectionPath;
276
+ private allDescendants;
277
+ _queryConstraints: {
278
+ where: Array<{
279
+ field: string;
280
+ op: string;
281
+ value: any;
282
+ }>;
283
+ orderBy?: string;
284
+ orderDirection?: string;
285
+ limit?: number;
286
+ offset?: number;
287
+ };
288
+ constructor(client: FirestoreClient, collectionPath: string, constraints: {
289
+ where: Array<{
290
+ field: string;
291
+ op: string;
292
+ value: any;
293
+ }>;
294
+ orderBy?: string;
295
+ orderDirection?: string;
296
+ limit?: number;
297
+ offset?: number;
298
+ }, allDescendants: boolean);
299
+ /**
300
+ * Add filter condition
301
+ * @param fieldPath Field path
302
+ * @param opStr Operator
303
+ * @param value Value
304
+ * @returns Query instance
305
+ */
306
+ where(fieldPath: string, opStr: string, value: any): Query;
307
+ /**
308
+ * Add sorting condition
309
+ * @param fieldPath Field path
310
+ * @param directionStr Sort direction ('asc' or 'desc')
311
+ * @returns Query instance
312
+ */
313
+ orderBy(fieldPath: string, directionStr?: "asc" | "desc"): Query;
314
+ /**
315
+ * Set limit on number of results
316
+ * @param limit Maximum number
317
+ * @returns Query instance
318
+ */
319
+ limit(limit: number): Query;
320
+ /**
321
+ * Set number of documents to skip
322
+ * @param offset Number to skip
323
+ * @returns Query instance
324
+ */
325
+ offset(offset: number): Query;
326
+ /**
327
+ * Execute query
328
+ * @returns QuerySnapshot instance
329
+ */
330
+ get(): Promise<QuerySnapshot>;
331
+ }
332
+ /**
333
+ * Query result class
334
+ */
335
+ export declare class QuerySnapshot {
336
+ private _docs;
337
+ constructor(results: Array<Record<string, any>>);
338
+ /**
339
+ * Array of documents in the result
340
+ */
341
+ get docs(): DocumentSnapshot[];
342
+ /**
343
+ * Whether the result is empty
344
+ */
345
+ get empty(): boolean;
346
+ /**
347
+ * Number of results
348
+ */
349
+ get size(): number;
350
+ /**
351
+ * Execute callback for each document
352
+ * @param callback Callback function to execute for each document
353
+ */
354
+ forEach(callback: (result: DocumentSnapshot) => void): void;
355
+ }
356
+ /**
357
+ * Document snapshot class
358
+ */
359
+ export declare class DocumentSnapshot {
360
+ private _id;
361
+ private _data;
362
+ constructor(id: string, data: Record<string, any> | null);
363
+ /**
364
+ * Document ID
365
+ */
366
+ get id(): string;
367
+ /**
368
+ * Whether the document exists
369
+ */
370
+ get exists(): boolean;
371
+ /**
372
+ * Get document data
373
+ * @returns Document data (undefined if it doesn't exist)
374
+ */
375
+ data(): Record<string, any> | undefined;
376
+ }
377
+ /**
378
+ * Write result class
379
+ */
380
+ export declare class WriteResult {
381
+ /**
382
+ * Write timestamp
383
+ */
384
+ readonly writeTime: Date;
385
+ constructor();
386
+ }
387
+ /**
388
+ * Create a new Firestore client instance
389
+ * @param config Firestore configuration object
390
+ * @returns FirestoreClient instance
391
+ *
392
+ * @example
393
+ * // Connect to default database
394
+ * const db = createFirestoreClient({
395
+ * projectId: 'your-project-id',
396
+ * privateKey: 'your-private-key',
397
+ * clientEmail: 'your-client-email'
398
+ * });
399
+ *
400
+ * // Connect to a different named database
401
+ * const customDb = createFirestoreClient({
402
+ * projectId: 'your-project-id',
403
+ * privateKey: 'your-private-key',
404
+ * clientEmail: 'your-client-email',
405
+ * databaseId: 'your-database-id'
406
+ * });
407
+ *
408
+ * // Connect to local emulator (no auth required)
409
+ * const emulatorDb = createFirestoreClient({
410
+ * projectId: 'demo-project',
411
+ * useEmulator: true,
412
+ * emulatorHost: '127.0.',
413
+ * emulatorPort: 8080,
414
+ * debug: true // Optional: enables detailed logging
415
+ * });
416
+ */
417
+ export declare function createFirestoreClient(config: FirestoreConfig): FirestoreClient;
@@ -0,0 +1,7 @@
1
+ export * from "./types";
2
+ import { FirestoreClient, createFirestoreClient, CollectionReference, DocumentReference, CollectionGroup, Query, QuerySnapshot, DocumentSnapshot, WriteResult } from "./client";
3
+ export { getFirestoreToken } from "./utils/auth";
4
+ export { convertToFirestoreValue, convertFromFirestoreValue, convertToFirestoreDocument, convertFromFirestoreDocument, } from "./utils/converter";
5
+ export { getFirestoreBasePath, getDocumentId } from "./utils/path";
6
+ export { formatPrivateKey } from "./utils/config";
7
+ export { FirestoreClient, createFirestoreClient, CollectionReference, DocumentReference, CollectionGroup, Query, QuerySnapshot, DocumentSnapshot, WriteResult, };