firebase-rest-firestore 0.3.1 → 1.1.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.
package/dist/client.d.ts CHANGED
@@ -33,6 +33,12 @@ export declare class FirestoreClient {
33
33
  * @returns DocumentReferenceインスタンス
34
34
  */
35
35
  doc(path: string): DocumentReference;
36
+ /**
37
+ * コレクショングループリファレンスを取得
38
+ * @param path コレクショングループのID
39
+ * @returns CollectionGroupインスタンス
40
+ */
41
+ collectionGroup(path: string): CollectionGroup;
36
42
  /**
37
43
  * Firestoreにドキュメントを追加
38
44
  * @param collectionName コレクション名
@@ -70,11 +76,12 @@ export declare class FirestoreClient {
70
76
  delete(collectionName: string, documentId: string): Promise<boolean>;
71
77
  /**
72
78
  * コレクションのドキュメントを検索
73
- * @param collectionName コレクション名
79
+ * @param collectionPath コレクションパス
74
80
  * @param options クエリオプション
81
+ * @param allDescendants 子孫コレクションを含むかどうか
75
82
  * @returns 検索結果のドキュメント配列
76
83
  */
77
- query(collectionName: string, options?: QueryOptions): Promise<(Record<string, any> & {
84
+ query(collectionPath: string, options: QueryOptions | undefined, allDescendants: boolean): Promise<(Record<string, any> & {
78
85
  id: string;
79
86
  })[]>;
80
87
  }
@@ -86,6 +93,10 @@ export declare class CollectionReference {
86
93
  private path;
87
94
  private _queryConstraints;
88
95
  constructor(client: FirestoreClient, path: string);
96
+ /**
97
+ * すべての子孫コレクションを含むかどうか
98
+ */
99
+ get allDescendants(): boolean;
89
100
  /**
90
101
  * ドキュメントリファレンスを取得
91
102
  * @param documentPath ドキュメントID(省略時は自動生成)
@@ -184,12 +195,58 @@ export declare class DocumentReference {
184
195
  */
185
196
  delete(): Promise<WriteResult>;
186
197
  }
198
+ /**
199
+ * コレクショングループ
200
+ */
201
+ export declare class CollectionGroup {
202
+ private client;
203
+ private path;
204
+ private _queryConstraints;
205
+ constructor(client: FirestoreClient, path: string);
206
+ /**
207
+ * すべての子孫コレクションを含むかどうか
208
+ */
209
+ get allDescendants(): boolean;
210
+ /**
211
+ * フィルター条件を追加
212
+ * @param fieldPath フィールドパス
213
+ * @param opStr 演算子
214
+ * @param value 値
215
+ * @returns Queryインスタンス
216
+ */
217
+ where(fieldPath: string, opStr: string, value: any): Query;
218
+ /**
219
+ * 並べ替え条件を追加
220
+ * @param fieldPath フィールドパス
221
+ * @param directionStr 並べ替え方向('asc'または'desc')
222
+ * @returns Queryインスタンス
223
+ */
224
+ orderBy(fieldPath: string, directionStr?: "asc" | "desc"): Query;
225
+ /**
226
+ * 取得件数の制限を設定
227
+ * @param limit 最大件数
228
+ * @returns Queryインスタンス
229
+ */
230
+ limit(limit: number): Query;
231
+ /**
232
+ * スキップ件数を設定
233
+ * @param offset スキップ件数
234
+ * @returns Queryインスタンス
235
+ */
236
+ offset(offset: number): Query;
237
+ /**
238
+ * クエリを実行
239
+ * @returns QuerySnapshotインスタンス
240
+ */
241
+ get(): Promise<QuerySnapshot>;
242
+ }
187
243
  /**
188
244
  * クエリクラス
189
245
  */
190
246
  export declare class Query {
191
247
  private client;
192
248
  private collectionPath;
249
+ private allDescendants;
193
250
  _queryConstraints: {
194
251
  where: Array<{
195
252
  field: string;
@@ -211,7 +268,7 @@ export declare class Query {
211
268
  orderDirection?: string;
212
269
  limit?: number;
213
270
  offset?: number;
214
- });
271
+ }, allDescendants: boolean);
215
272
  /**
216
273
  * フィルター条件を追加
217
274
  * @param fieldPath フィールドパス
package/dist/client.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WriteResult = exports.DocumentSnapshot = exports.QuerySnapshot = exports.Query = exports.DocumentReference = exports.CollectionReference = exports.FirestoreClient = void 0;
3
+ exports.WriteResult = exports.DocumentSnapshot = exports.QuerySnapshot = exports.Query = exports.CollectionGroup = exports.DocumentReference = exports.CollectionReference = exports.FirestoreClient = void 0;
4
4
  exports.createFirestoreClient = createFirestoreClient;
5
5
  const auth_1 = require("./utils/auth");
6
6
  const converter_1 = require("./utils/converter");
@@ -81,6 +81,14 @@ class FirestoreClient {
81
81
  const docId = parts[parts.length - 1];
82
82
  return new DocumentReference(this, collectionPath, docId);
83
83
  }
84
+ /**
85
+ * コレクショングループリファレンスを取得
86
+ * @param path コレクショングループのID
87
+ * @returns CollectionGroupインスタンス
88
+ */
89
+ collectionGroup(path) {
90
+ return new CollectionGroup(this, path);
91
+ }
84
92
  /**
85
93
  * Firestoreにドキュメントを追加
86
94
  * @param collectionName コレクション名
@@ -184,20 +192,35 @@ class FirestoreClient {
184
192
  }
185
193
  /**
186
194
  * コレクションのドキュメントを検索
187
- * @param collectionName コレクション名
195
+ * @param collectionPath コレクションパス
188
196
  * @param options クエリオプション
197
+ * @param allDescendants 子孫コレクションを含むかどうか
189
198
  * @returns 検索結果のドキュメント配列
190
199
  */
191
- async query(collectionName, options = {}) {
200
+ async query(collectionPath, options = {}, allDescendants) {
192
201
  // 操作前に設定をチェック
193
202
  this.checkConfig();
194
- // 修正: URLの形式を修正
195
203
  // :runQueryはコレクション単位ではなく、ドキュメントルート単位で実行
196
- const basePath = `https://firestore.googleapis.com/v1/projects/${this.config.projectId}/databases/(default)/documents`;
204
+ // コレクション名パス形式の場合は、endpointをドキュメントルートに置き換える。collectionNameはコレクションIDにする
205
+ // e.g) パス形式: collectionPath = "/users/123/items" の場合は、collectionPaths = ["users", "123", "items"], collectionName = "items", basePath += "/users/123"
206
+ // e.g) 単一形式: collectionPath = "items" の場合は、collectionPaths = ["items"], collectionName = "items", basePath += ""
207
+ const collectionPaths = collectionPath.split("/");
208
+ let basePath = `https://firestore.googleapis.com/v1/projects/${this.config.projectId}/databases/(default)/documents`;
209
+ let collectionName = collectionPath;
210
+ if (collectionPaths.length > 1) {
211
+ basePath = `${basePath}/${collectionPaths.slice(0, -1).join("/")}`;
212
+ collectionName = collectionPaths[collectionPaths.length - 1];
213
+ }
197
214
  const url = `${basePath}:runQuery`;
198
215
  // クエリ構築
216
+ //allDescendants: falseの場合は直下のcollectionを参照。trueの場合は子孫コレクションも参照
199
217
  const structuredQuery = {
200
- from: [{ collectionId: collectionName }],
218
+ from: [
219
+ {
220
+ collectionId: collectionName,
221
+ allDescendants,
222
+ },
223
+ ],
201
224
  };
202
225
  // フィルター条件
203
226
  if (options.where && options.where.length > 0) {
@@ -291,6 +314,12 @@ class CollectionReference {
291
314
  where: [],
292
315
  };
293
316
  }
317
+ /**
318
+ * すべての子孫コレクションを含むかどうか
319
+ */
320
+ get allDescendants() {
321
+ return false;
322
+ }
294
323
  /**
295
324
  * ドキュメントリファレンスを取得
296
325
  * @param documentPath ドキュメントID(省略時は自動生成)
@@ -320,7 +349,7 @@ class CollectionReference {
320
349
  where(fieldPath, opStr, value) {
321
350
  const query = new Query(this.client, this.path, {
322
351
  ...this._queryConstraints,
323
- });
352
+ }, this.allDescendants);
324
353
  // 演算子の変換
325
354
  let firestoreOp;
326
355
  switch (opStr) {
@@ -373,7 +402,7 @@ class CollectionReference {
373
402
  orderBy(fieldPath, directionStr = "asc") {
374
403
  const query = new Query(this.client, this.path, {
375
404
  ...this._queryConstraints,
376
- });
405
+ }, this.allDescendants);
377
406
  query._queryConstraints.orderBy = fieldPath;
378
407
  query._queryConstraints.orderDirection =
379
408
  directionStr === "asc" ? "ASCENDING" : "DESCENDING";
@@ -387,7 +416,7 @@ class CollectionReference {
387
416
  limit(limit) {
388
417
  const query = new Query(this.client, this.path, {
389
418
  ...this._queryConstraints,
390
- });
419
+ }, this.allDescendants);
391
420
  query._queryConstraints.limit = limit;
392
421
  return query;
393
422
  }
@@ -399,7 +428,7 @@ class CollectionReference {
399
428
  offset(offset) {
400
429
  const query = new Query(this.client, this.path, {
401
430
  ...this._queryConstraints,
402
- });
431
+ }, this.allDescendants);
403
432
  query._queryConstraints.offset = offset;
404
433
  return query;
405
434
  }
@@ -408,7 +437,7 @@ class CollectionReference {
408
437
  * @returns QuerySnapshotインスタンス
409
438
  */
410
439
  async get() {
411
- const results = await this.client.query(this.path, this._queryConstraints);
440
+ const results = await this.client.query(this.path, this._queryConstraints, this.allDescendants);
412
441
  return new QuerySnapshot(results);
413
442
  }
414
443
  /**
@@ -503,14 +532,135 @@ class DocumentReference {
503
532
  }
504
533
  }
505
534
  exports.DocumentReference = DocumentReference;
535
+ /**
536
+ * コレクショングループ
537
+ */
538
+ class CollectionGroup {
539
+ constructor(client, path) {
540
+ this.client = client;
541
+ this.path = path;
542
+ this._queryConstraints = {
543
+ where: [],
544
+ };
545
+ }
546
+ /**
547
+ * すべての子孫コレクションを含むかどうか
548
+ */
549
+ get allDescendants() {
550
+ return true;
551
+ }
552
+ /**
553
+ * フィルター条件を追加
554
+ * @param fieldPath フィールドパス
555
+ * @param opStr 演算子
556
+ * @param value 値
557
+ * @returns Queryインスタンス
558
+ */
559
+ where(fieldPath, opStr, value) {
560
+ const query = new Query(this.client, this.path, {
561
+ ...this._queryConstraints,
562
+ }, this.allDescendants);
563
+ // 演算子の変換
564
+ let firestoreOp;
565
+ switch (opStr) {
566
+ case "==":
567
+ firestoreOp = "EQUAL";
568
+ break;
569
+ case "!=":
570
+ firestoreOp = "NOT_EQUAL";
571
+ break;
572
+ case "<":
573
+ firestoreOp = "LESS_THAN";
574
+ break;
575
+ case "<=":
576
+ firestoreOp = "LESS_THAN_OR_EQUAL";
577
+ break;
578
+ case ">":
579
+ firestoreOp = "GREATER_THAN";
580
+ break;
581
+ case ">=":
582
+ firestoreOp = "GREATER_THAN_OR_EQUAL";
583
+ break;
584
+ case "array-contains":
585
+ firestoreOp = "ARRAY_CONTAINS";
586
+ break;
587
+ case "in":
588
+ firestoreOp = "IN";
589
+ break;
590
+ case "array-contains-any":
591
+ firestoreOp = "ARRAY_CONTAINS_ANY";
592
+ break;
593
+ case "not-in":
594
+ firestoreOp = "NOT_IN";
595
+ break;
596
+ default:
597
+ firestoreOp = opStr;
598
+ }
599
+ query._queryConstraints.where.push({
600
+ field: fieldPath,
601
+ op: firestoreOp,
602
+ value,
603
+ });
604
+ return query;
605
+ }
606
+ /**
607
+ * 並べ替え条件を追加
608
+ * @param fieldPath フィールドパス
609
+ * @param directionStr 並べ替え方向('asc'または'desc')
610
+ * @returns Queryインスタンス
611
+ */
612
+ orderBy(fieldPath, directionStr = "asc") {
613
+ const query = new Query(this.client, this.path, {
614
+ ...this._queryConstraints,
615
+ }, this.allDescendants);
616
+ query._queryConstraints.orderBy = fieldPath;
617
+ query._queryConstraints.orderDirection =
618
+ directionStr === "asc" ? "ASCENDING" : "DESCENDING";
619
+ return query;
620
+ }
621
+ /**
622
+ * 取得件数の制限を設定
623
+ * @param limit 最大件数
624
+ * @returns Queryインスタンス
625
+ */
626
+ limit(limit) {
627
+ const query = new Query(this.client, this.path, {
628
+ ...this._queryConstraints,
629
+ }, this.allDescendants);
630
+ query._queryConstraints.limit = limit;
631
+ return query;
632
+ }
633
+ /**
634
+ * スキップ件数を設定
635
+ * @param offset スキップ件数
636
+ * @returns Queryインスタンス
637
+ */
638
+ offset(offset) {
639
+ const query = new Query(this.client, this.path, {
640
+ ...this._queryConstraints,
641
+ }, this.allDescendants);
642
+ query._queryConstraints.offset = offset;
643
+ return query;
644
+ }
645
+ /**
646
+ * クエリを実行
647
+ * @returns QuerySnapshotインスタンス
648
+ */
649
+ async get() {
650
+ const results = await this.client.query(this.path, this._queryConstraints, this.allDescendants);
651
+ return new QuerySnapshot(results);
652
+ }
653
+ }
654
+ exports.CollectionGroup = CollectionGroup;
506
655
  /**
507
656
  * クエリクラス
508
657
  */
509
658
  class Query {
510
- constructor(client, collectionPath, constraints) {
659
+ constructor(client, collectionPath, constraints, allDescendants) {
511
660
  this.client = client;
512
661
  this.collectionPath = collectionPath;
513
662
  this._queryConstraints = constraints;
663
+ this.allDescendants = allDescendants;
514
664
  }
515
665
  /**
516
666
  * フィルター条件を追加
@@ -522,7 +672,7 @@ class Query {
522
672
  where(fieldPath, opStr, value) {
523
673
  const query = new Query(this.client, this.collectionPath, {
524
674
  ...this._queryConstraints,
525
- });
675
+ }, this.allDescendants);
526
676
  // 演算子の変換
527
677
  let firestoreOp;
528
678
  switch (opStr) {
@@ -575,7 +725,7 @@ class Query {
575
725
  orderBy(fieldPath, directionStr = "asc") {
576
726
  const query = new Query(this.client, this.collectionPath, {
577
727
  ...this._queryConstraints,
578
- });
728
+ }, this.allDescendants);
579
729
  query._queryConstraints.orderBy = fieldPath;
580
730
  query._queryConstraints.orderDirection =
581
731
  directionStr === "asc" ? "ASCENDING" : "DESCENDING";
@@ -589,7 +739,7 @@ class Query {
589
739
  limit(limit) {
590
740
  const query = new Query(this.client, this.collectionPath, {
591
741
  ...this._queryConstraints,
592
- });
742
+ }, this.allDescendants);
593
743
  query._queryConstraints.limit = limit;
594
744
  return query;
595
745
  }
@@ -601,7 +751,7 @@ class Query {
601
751
  offset(offset) {
602
752
  const query = new Query(this.client, this.collectionPath, {
603
753
  ...this._queryConstraints,
604
- });
754
+ }, this.allDescendants);
605
755
  query._queryConstraints.offset = offset;
606
756
  return query;
607
757
  }
@@ -610,7 +760,7 @@ class Query {
610
760
  * @returns QuerySnapshotインスタンス
611
761
  */
612
762
  async get() {
613
- const results = await this.client.query(this.collectionPath, this._queryConstraints);
763
+ const results = await this.client.query(this.collectionPath, this._queryConstraints, this.allDescendants);
614
764
  return new QuerySnapshot(results);
615
765
  }
616
766
  }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export * from "./types";
2
- import { FirestoreClient, createFirestoreClient, CollectionReference, DocumentReference, Query, QuerySnapshot, DocumentSnapshot, WriteResult } from "./client";
2
+ import { FirestoreClient, createFirestoreClient, CollectionReference, DocumentReference, CollectionGroup, Query, QuerySnapshot, DocumentSnapshot, WriteResult } from "./client";
3
3
  export { getFirestoreToken } from "./utils/auth";
4
4
  export { convertToFirestoreValue, convertFromFirestoreValue, convertToFirestoreDocument, convertFromFirestoreDocument, } from "./utils/converter";
5
5
  export { getFirestoreBasePath, getDocumentId } from "./utils/path";
6
6
  export { formatPrivateKey, formatConfig } from "./utils/config";
7
- export { FirestoreClient, createFirestoreClient, CollectionReference, DocumentReference, Query, QuerySnapshot, DocumentSnapshot, WriteResult, };
7
+ export { FirestoreClient, createFirestoreClient, CollectionReference, DocumentReference, CollectionGroup, Query, QuerySnapshot, DocumentSnapshot, WriteResult, };
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.WriteResult = exports.DocumentSnapshot = exports.QuerySnapshot = exports.Query = exports.DocumentReference = exports.CollectionReference = exports.createFirestoreClient = exports.FirestoreClient = exports.formatConfig = exports.formatPrivateKey = exports.getDocumentId = exports.getFirestoreBasePath = exports.convertFromFirestoreDocument = exports.convertToFirestoreDocument = exports.convertFromFirestoreValue = exports.convertToFirestoreValue = exports.getFirestoreToken = void 0;
17
+ exports.WriteResult = exports.DocumentSnapshot = exports.QuerySnapshot = exports.Query = exports.CollectionGroup = exports.DocumentReference = exports.CollectionReference = exports.createFirestoreClient = exports.FirestoreClient = exports.formatConfig = exports.formatPrivateKey = exports.getDocumentId = exports.getFirestoreBasePath = exports.convertFromFirestoreDocument = exports.convertToFirestoreDocument = exports.convertFromFirestoreValue = exports.convertToFirestoreValue = exports.getFirestoreToken = void 0;
18
18
  // 型定義のエクスポート
19
19
  __exportStar(require("./types"), exports);
20
20
  // クライアントのエクスポート
@@ -23,6 +23,7 @@ Object.defineProperty(exports, "FirestoreClient", { enumerable: true, get: funct
23
23
  Object.defineProperty(exports, "createFirestoreClient", { enumerable: true, get: function () { return client_1.createFirestoreClient; } });
24
24
  Object.defineProperty(exports, "CollectionReference", { enumerable: true, get: function () { return client_1.CollectionReference; } });
25
25
  Object.defineProperty(exports, "DocumentReference", { enumerable: true, get: function () { return client_1.DocumentReference; } });
26
+ Object.defineProperty(exports, "CollectionGroup", { enumerable: true, get: function () { return client_1.CollectionGroup; } });
26
27
  Object.defineProperty(exports, "Query", { enumerable: true, get: function () { return client_1.Query; } });
27
28
  Object.defineProperty(exports, "QuerySnapshot", { enumerable: true, get: function () { return client_1.QuerySnapshot; } });
28
29
  Object.defineProperty(exports, "DocumentSnapshot", { enumerable: true, get: function () { return client_1.DocumentSnapshot; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-rest-firestore",
3
- "version": "0.3.1",
3
+ "version": "1.1.0",
4
4
  "description": "Firebase Firestore REST API client for Edge runtime environments",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -29,13 +29,16 @@
29
29
  "jose": "^4.14.4"
30
30
  },
31
31
  "devDependencies": {
32
+ "@semantic-release/changelog": "^6.0.3",
33
+ "@semantic-release/git": "^10.0.1",
32
34
  "@types/node": "^18.16.0",
33
35
  "dotenv": "^16.4.7",
36
+ "semantic-release": "^24.2.3",
34
37
  "typescript": "^5.0.4",
35
38
  "vitest": "^3.0.9"
36
39
  },
37
40
  "engines": {
38
- "node": ">=16.0.0"
41
+ "node": ">=20.8.1"
39
42
  },
40
43
  "repository": {
41
44
  "type": "git",
@@ -1,59 +0,0 @@
1
- /**
2
- * Firestoreの認証トークンを取得する関数
3
- */
4
- export declare function getFirestoreToken(): Promise<string>;
5
- /**
6
- * Firestoreクライアントクラス
7
- */
8
- export declare class FirestoreClient {
9
- private token;
10
- private tokenExpiry;
11
- /**
12
- * 認証トークンを取得(キャッシュあり)
13
- */
14
- private getToken;
15
- /**
16
- * Firestoreにドキュメントを作成
17
- */
18
- create(collectionName: string, data: Record<string, any>): Promise<Record<string, any> & {
19
- id: string;
20
- }>;
21
- /**
22
- * ドキュメントを取得
23
- */
24
- get(collectionName: string, documentId: string): Promise<(Record<string, any> & {
25
- id: string;
26
- }) | null>;
27
- /**
28
- * ドキュメントを更新
29
- */
30
- update(collectionName: string, documentId: string, data: Record<string, any>): Promise<Record<string, any> & {
31
- id: string;
32
- }>;
33
- /**
34
- * ドキュメントを削除
35
- */
36
- delete(collectionName: string, documentId: string): Promise<boolean>;
37
- /**
38
- * コレクションのドキュメントを検索
39
- */
40
- query(collectionName: string, options?: {
41
- where?: Array<{
42
- field: string;
43
- op: string;
44
- value: any;
45
- }>;
46
- orderBy?: string;
47
- limit?: number;
48
- offset?: number;
49
- }): Promise<(Record<string, any> & {
50
- id: string;
51
- })[]>;
52
- }
53
- export declare const firestore: FirestoreClient;
54
- export declare function saveToFirestore(collectionName: string, data: Record<string, any>): Promise<{
55
- documentId: string;
56
- result: Record<string, any> & {
57
- id: string;
58
- };
59
- }>;
package/dist/cjs/index.js DELETED
@@ -1,388 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.firestore = exports.FirestoreClient = void 0;
37
- exports.getFirestoreToken = getFirestoreToken;
38
- exports.saveToFirestore = saveToFirestore;
39
- const jose = __importStar(require("jose"));
40
- // Firestore関連の定数
41
- const FIREBASE_PROJECT_ID = process.env.FIREBASE_PROJECT_ID;
42
- const FIREBASE_PRIVATE_KEY_ID = process.env.FIREBASE_PRIVATE_KEY_ID;
43
- const FIREBASE_PRIVATE_KEY = process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, "\n");
44
- const FIREBASE_CLIENT_EMAIL = process.env.FIREBASE_CLIENT_EMAIL;
45
- const FIREBASE_CLIENT_ID = process.env.FIREBASE_CLIENT_ID;
46
- const FIREBASE_CLIENT_CERT_URL = process.env.FIREBASE_CLIENT_CERT_URL;
47
- // サービスアカウント情報
48
- const serviceAccount = {
49
- type: "service_account",
50
- project_id: FIREBASE_PROJECT_ID,
51
- private_key_id: FIREBASE_PRIVATE_KEY_ID,
52
- private_key: FIREBASE_PRIVATE_KEY,
53
- client_email: FIREBASE_CLIENT_EMAIL,
54
- client_id: FIREBASE_CLIENT_ID,
55
- auth_uri: "https://accounts.google.com/o/oauth2/auth",
56
- token_uri: "https://oauth2.googleapis.com/token",
57
- auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs",
58
- client_x509_cert_url: FIREBASE_CLIENT_CERT_URL,
59
- };
60
- // Firestoreのベースパス
61
- const getFirestoreBasePath = (collectionName) => {
62
- const basePath = `https://firestore.googleapis.com/v1/projects/${FIREBASE_PROJECT_ID}/databases/(default)/documents`;
63
- return collectionName ? `${basePath}/${collectionName}` : basePath;
64
- };
65
- /**
66
- * JSの値をFirestore形式に変換する
67
- */
68
- function convertToFirestoreValue(value) {
69
- if (value instanceof Date) {
70
- return { timestampValue: value.toISOString() };
71
- }
72
- else if (typeof value === "string") {
73
- return { stringValue: value };
74
- }
75
- else if (typeof value === "number") {
76
- return Number.isInteger(value)
77
- ? { integerValue: value }
78
- : { doubleValue: value };
79
- }
80
- else if (typeof value === "boolean") {
81
- return { booleanValue: value };
82
- }
83
- else if (value === null || value === undefined) {
84
- return { nullValue: null };
85
- }
86
- else if (Array.isArray(value)) {
87
- return {
88
- arrayValue: {
89
- values: value.map(item => convertToFirestoreValue(item)),
90
- },
91
- };
92
- }
93
- else if (typeof value === "object") {
94
- const fields = Object.entries(value).reduce((acc, [key, val]) => ({
95
- ...acc,
96
- [key]: convertToFirestoreValue(val),
97
- }), {});
98
- return { mapValue: { fields } };
99
- }
100
- // デフォルトは文字列化
101
- return { stringValue: String(value) };
102
- }
103
- /**
104
- * Firestore形式からJSの値に変換する
105
- */
106
- function convertFromFirestoreValue(firestoreValue) {
107
- if ("stringValue" in firestoreValue) {
108
- return firestoreValue.stringValue;
109
- }
110
- else if ("integerValue" in firestoreValue) {
111
- return Number(firestoreValue.integerValue);
112
- }
113
- else if ("doubleValue" in firestoreValue) {
114
- return firestoreValue.doubleValue;
115
- }
116
- else if ("booleanValue" in firestoreValue) {
117
- return firestoreValue.booleanValue;
118
- }
119
- else if ("nullValue" in firestoreValue) {
120
- return null;
121
- }
122
- else if ("timestampValue" in firestoreValue) {
123
- return new Date(firestoreValue.timestampValue);
124
- }
125
- else if ("mapValue" in firestoreValue && firestoreValue.mapValue.fields) {
126
- return Object.entries(firestoreValue.mapValue.fields).reduce((acc, [key, val]) => ({
127
- ...acc,
128
- [key]: convertFromFirestoreValue(val),
129
- }), {});
130
- }
131
- else if ("arrayValue" in firestoreValue &&
132
- firestoreValue.arrayValue.values) {
133
- return firestoreValue.arrayValue.values.map(convertFromFirestoreValue);
134
- }
135
- return null;
136
- }
137
- /**
138
- * オブジェクトをFirestoreドキュメント形式に変換
139
- */
140
- function convertToFirestoreDocument(data) {
141
- return {
142
- fields: Object.entries(data).reduce((acc, [key, value]) => ({
143
- ...acc,
144
- [key]: convertToFirestoreValue(value),
145
- }), {}),
146
- };
147
- }
148
- /**
149
- * Firestoreドキュメントをオブジェクトに変換
150
- */
151
- function convertFromFirestoreDocument(doc) {
152
- if (!doc.fields)
153
- return { id: getDocumentId(doc.name) };
154
- const result = Object.entries(doc.fields).reduce((acc, [key, value]) => ({
155
- ...acc,
156
- [key]: convertFromFirestoreValue(value),
157
- }), {});
158
- return {
159
- ...result,
160
- id: getDocumentId(doc.name),
161
- };
162
- }
163
- /**
164
- * ドキュメントパスからIDを抽出
165
- */
166
- function getDocumentId(path) {
167
- if (!path)
168
- return "";
169
- const pathParts = path.split("/");
170
- return pathParts[pathParts.length - 1];
171
- }
172
- // JWT(JSON Web Token)を作成する関数
173
- async function createJWT(serviceAccount) {
174
- const now = Math.floor(Date.now() / 1000);
175
- const payload = {
176
- iss: serviceAccount.client_email,
177
- sub: serviceAccount.client_email,
178
- aud: "https://oauth2.googleapis.com/token",
179
- iat: now,
180
- exp: now + 3600, // 1時間後に期限切れ
181
- scope: "https://www.googleapis.com/auth/datastore",
182
- };
183
- try {
184
- // 秘密鍵をインポート
185
- const privateKey = await jose.importPKCS8(serviceAccount.private_key, "RS256");
186
- // JWTを作成
187
- const token = await new jose.SignJWT(payload)
188
- .setProtectedHeader({
189
- alg: "RS256",
190
- kid: serviceAccount.private_key_id,
191
- typ: "JWT",
192
- })
193
- .sign(privateKey);
194
- return token;
195
- }
196
- catch (error) {
197
- console.error("JWT作成エラー:", error);
198
- throw error;
199
- }
200
- }
201
- /**
202
- * Firestoreの認証トークンを取得する関数
203
- */
204
- async function getFirestoreToken() {
205
- // トークンを取得するためのリクエスト
206
- const response = await fetch("https://oauth2.googleapis.com/token", {
207
- method: "POST",
208
- headers: {
209
- "Content-Type": "application/json",
210
- },
211
- body: JSON.stringify({
212
- grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
213
- assertion: await createJWT(serviceAccount),
214
- }),
215
- });
216
- const data = (await response.json());
217
- return data.access_token;
218
- }
219
- /**
220
- * Firestoreクライアントクラス
221
- */
222
- class FirestoreClient {
223
- constructor() {
224
- this.token = null;
225
- this.tokenExpiry = 0;
226
- }
227
- /**
228
- * 認証トークンを取得(キャッシュあり)
229
- */
230
- async getToken() {
231
- const now = Date.now();
232
- // トークンが期限切れか未取得の場合は新しく取得
233
- if (!this.token || now >= this.tokenExpiry) {
234
- this.token = await getFirestoreToken();
235
- // 50分後に期限切れとする(実際は1時間)
236
- this.tokenExpiry = now + 50 * 60 * 1000;
237
- }
238
- return this.token;
239
- }
240
- /**
241
- * Firestoreにドキュメントを作成
242
- */
243
- async create(collectionName, data) {
244
- const url = getFirestoreBasePath(collectionName);
245
- const firestoreData = convertToFirestoreDocument(data);
246
- const token = await this.getToken();
247
- const response = await fetch(url, {
248
- method: "POST",
249
- headers: {
250
- "Content-Type": "application/json",
251
- Authorization: `Bearer ${token}`,
252
- },
253
- body: JSON.stringify(firestoreData),
254
- });
255
- if (!response.ok) {
256
- throw new Error(`Firestore API error: ${response.statusText}`);
257
- }
258
- const result = (await response.json());
259
- return convertFromFirestoreDocument(result);
260
- }
261
- /**
262
- * ドキュメントを取得
263
- */
264
- async get(collectionName, documentId) {
265
- const url = `${getFirestoreBasePath(collectionName)}/${documentId}`;
266
- const token = await this.getToken();
267
- const response = await fetch(url, {
268
- method: "GET",
269
- headers: {
270
- Authorization: `Bearer ${token}`,
271
- },
272
- });
273
- if (response.status === 404) {
274
- return null;
275
- }
276
- if (!response.ok) {
277
- throw new Error(`Firestore API error: ${response.statusText}`);
278
- }
279
- const result = (await response.json());
280
- return convertFromFirestoreDocument(result);
281
- }
282
- /**
283
- * ドキュメントを更新
284
- */
285
- async update(collectionName, documentId, data) {
286
- const url = `${getFirestoreBasePath(collectionName)}/${documentId}`;
287
- const firestoreData = convertToFirestoreDocument(data);
288
- const token = await this.getToken();
289
- const response = await fetch(url, {
290
- method: "PATCH",
291
- headers: {
292
- "Content-Type": "application/json",
293
- Authorization: `Bearer ${token}`,
294
- },
295
- body: JSON.stringify(firestoreData),
296
- });
297
- if (!response.ok) {
298
- throw new Error(`Firestore API error: ${response.statusText}`);
299
- }
300
- const result = (await response.json());
301
- return convertFromFirestoreDocument(result);
302
- }
303
- /**
304
- * ドキュメントを削除
305
- */
306
- async delete(collectionName, documentId) {
307
- const url = `${getFirestoreBasePath(collectionName)}/${documentId}`;
308
- const token = await this.getToken();
309
- const response = await fetch(url, {
310
- method: "DELETE",
311
- headers: {
312
- Authorization: `Bearer ${token}`,
313
- },
314
- });
315
- if (!response.ok) {
316
- throw new Error(`Firestore API error: ${response.statusText}`);
317
- }
318
- return true;
319
- }
320
- /**
321
- * コレクションのドキュメントを検索
322
- */
323
- async query(collectionName, options = {}) {
324
- const url = `${getFirestoreBasePath(collectionName)}:runQuery`;
325
- // クエリ構築
326
- const structuredQuery = {
327
- from: [{ collectionId: collectionName }],
328
- };
329
- // フィルター条件
330
- if (options.where && options.where.length > 0) {
331
- structuredQuery.where = {
332
- compositeFilter: {
333
- op: "AND",
334
- filters: options.where.map(condition => ({
335
- fieldFilter: {
336
- field: { fieldPath: condition.field },
337
- op: condition.op,
338
- value: convertToFirestoreValue(condition.value),
339
- },
340
- })),
341
- },
342
- };
343
- }
344
- // 並べ替え
345
- if (options.orderBy) {
346
- structuredQuery.orderBy = [
347
- {
348
- field: { fieldPath: options.orderBy },
349
- direction: "ASCENDING",
350
- },
351
- ];
352
- }
353
- // 制限
354
- if (options.limit) {
355
- structuredQuery.limit = options.limit;
356
- }
357
- // オフセット
358
- if (options.offset) {
359
- structuredQuery.offset = options.offset;
360
- }
361
- const token = await this.getToken();
362
- const response = await fetch(url, {
363
- method: "POST",
364
- headers: {
365
- "Content-Type": "application/json",
366
- Authorization: `Bearer ${token}`,
367
- },
368
- body: JSON.stringify({
369
- structuredQuery,
370
- }),
371
- });
372
- if (!response.ok) {
373
- throw new Error(`Firestore API error: ${response.statusText}`);
374
- }
375
- const results = (await response.json());
376
- return results
377
- .filter(item => item.document)
378
- .map(item => convertFromFirestoreDocument(item.document));
379
- }
380
- }
381
- exports.FirestoreClient = FirestoreClient;
382
- // デフォルトのFirestoreクライアントインスタンス
383
- exports.firestore = new FirestoreClient();
384
- // 後方互換性のための関数
385
- async function saveToFirestore(collectionName, data) {
386
- const result = await exports.firestore.create(collectionName, data);
387
- return { documentId: result.id, result };
388
- }
@@ -1,59 +0,0 @@
1
- /**
2
- * Firestoreの認証トークンを取得する関数
3
- */
4
- export declare function getFirestoreToken(): Promise<string>;
5
- /**
6
- * Firestoreクライアントクラス
7
- */
8
- export declare class FirestoreClient {
9
- private token;
10
- private tokenExpiry;
11
- /**
12
- * 認証トークンを取得(キャッシュあり)
13
- */
14
- private getToken;
15
- /**
16
- * Firestoreにドキュメントを作成
17
- */
18
- create(collectionName: string, data: Record<string, any>): Promise<Record<string, any> & {
19
- id: string;
20
- }>;
21
- /**
22
- * ドキュメントを取得
23
- */
24
- get(collectionName: string, documentId: string): Promise<(Record<string, any> & {
25
- id: string;
26
- }) | null>;
27
- /**
28
- * ドキュメントを更新
29
- */
30
- update(collectionName: string, documentId: string, data: Record<string, any>): Promise<Record<string, any> & {
31
- id: string;
32
- }>;
33
- /**
34
- * ドキュメントを削除
35
- */
36
- delete(collectionName: string, documentId: string): Promise<boolean>;
37
- /**
38
- * コレクションのドキュメントを検索
39
- */
40
- query(collectionName: string, options?: {
41
- where?: Array<{
42
- field: string;
43
- op: string;
44
- value: any;
45
- }>;
46
- orderBy?: string;
47
- limit?: number;
48
- offset?: number;
49
- }): Promise<(Record<string, any> & {
50
- id: string;
51
- })[]>;
52
- }
53
- export declare const firestore: FirestoreClient;
54
- export declare function saveToFirestore(collectionName: string, data: Record<string, any>): Promise<{
55
- documentId: string;
56
- result: Record<string, any> & {
57
- id: string;
58
- };
59
- }>;
package/dist/esm/index.js DELETED
@@ -1,349 +0,0 @@
1
- import * as jose from "jose";
2
- // Firestore関連の定数
3
- const FIREBASE_PROJECT_ID = process.env.FIREBASE_PROJECT_ID;
4
- const FIREBASE_PRIVATE_KEY_ID = process.env.FIREBASE_PRIVATE_KEY_ID;
5
- const FIREBASE_PRIVATE_KEY = process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, "\n");
6
- const FIREBASE_CLIENT_EMAIL = process.env.FIREBASE_CLIENT_EMAIL;
7
- const FIREBASE_CLIENT_ID = process.env.FIREBASE_CLIENT_ID;
8
- const FIREBASE_CLIENT_CERT_URL = process.env.FIREBASE_CLIENT_CERT_URL;
9
- // サービスアカウント情報
10
- const serviceAccount = {
11
- type: "service_account",
12
- project_id: FIREBASE_PROJECT_ID,
13
- private_key_id: FIREBASE_PRIVATE_KEY_ID,
14
- private_key: FIREBASE_PRIVATE_KEY,
15
- client_email: FIREBASE_CLIENT_EMAIL,
16
- client_id: FIREBASE_CLIENT_ID,
17
- auth_uri: "https://accounts.google.com/o/oauth2/auth",
18
- token_uri: "https://oauth2.googleapis.com/token",
19
- auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs",
20
- client_x509_cert_url: FIREBASE_CLIENT_CERT_URL,
21
- };
22
- // Firestoreのベースパス
23
- const getFirestoreBasePath = (collectionName) => {
24
- const basePath = `https://firestore.googleapis.com/v1/projects/${FIREBASE_PROJECT_ID}/databases/(default)/documents`;
25
- return collectionName ? `${basePath}/${collectionName}` : basePath;
26
- };
27
- /**
28
- * JSの値をFirestore形式に変換する
29
- */
30
- function convertToFirestoreValue(value) {
31
- if (value instanceof Date) {
32
- return { timestampValue: value.toISOString() };
33
- }
34
- else if (typeof value === "string") {
35
- return { stringValue: value };
36
- }
37
- else if (typeof value === "number") {
38
- return Number.isInteger(value)
39
- ? { integerValue: value }
40
- : { doubleValue: value };
41
- }
42
- else if (typeof value === "boolean") {
43
- return { booleanValue: value };
44
- }
45
- else if (value === null || value === undefined) {
46
- return { nullValue: null };
47
- }
48
- else if (Array.isArray(value)) {
49
- return {
50
- arrayValue: {
51
- values: value.map(item => convertToFirestoreValue(item)),
52
- },
53
- };
54
- }
55
- else if (typeof value === "object") {
56
- const fields = Object.entries(value).reduce((acc, [key, val]) => ({
57
- ...acc,
58
- [key]: convertToFirestoreValue(val),
59
- }), {});
60
- return { mapValue: { fields } };
61
- }
62
- // デフォルトは文字列化
63
- return { stringValue: String(value) };
64
- }
65
- /**
66
- * Firestore形式からJSの値に変換する
67
- */
68
- function convertFromFirestoreValue(firestoreValue) {
69
- if ("stringValue" in firestoreValue) {
70
- return firestoreValue.stringValue;
71
- }
72
- else if ("integerValue" in firestoreValue) {
73
- return Number(firestoreValue.integerValue);
74
- }
75
- else if ("doubleValue" in firestoreValue) {
76
- return firestoreValue.doubleValue;
77
- }
78
- else if ("booleanValue" in firestoreValue) {
79
- return firestoreValue.booleanValue;
80
- }
81
- else if ("nullValue" in firestoreValue) {
82
- return null;
83
- }
84
- else if ("timestampValue" in firestoreValue) {
85
- return new Date(firestoreValue.timestampValue);
86
- }
87
- else if ("mapValue" in firestoreValue && firestoreValue.mapValue.fields) {
88
- return Object.entries(firestoreValue.mapValue.fields).reduce((acc, [key, val]) => ({
89
- ...acc,
90
- [key]: convertFromFirestoreValue(val),
91
- }), {});
92
- }
93
- else if ("arrayValue" in firestoreValue &&
94
- firestoreValue.arrayValue.values) {
95
- return firestoreValue.arrayValue.values.map(convertFromFirestoreValue);
96
- }
97
- return null;
98
- }
99
- /**
100
- * オブジェクトをFirestoreドキュメント形式に変換
101
- */
102
- function convertToFirestoreDocument(data) {
103
- return {
104
- fields: Object.entries(data).reduce((acc, [key, value]) => ({
105
- ...acc,
106
- [key]: convertToFirestoreValue(value),
107
- }), {}),
108
- };
109
- }
110
- /**
111
- * Firestoreドキュメントをオブジェクトに変換
112
- */
113
- function convertFromFirestoreDocument(doc) {
114
- if (!doc.fields)
115
- return { id: getDocumentId(doc.name) };
116
- const result = Object.entries(doc.fields).reduce((acc, [key, value]) => ({
117
- ...acc,
118
- [key]: convertFromFirestoreValue(value),
119
- }), {});
120
- return {
121
- ...result,
122
- id: getDocumentId(doc.name),
123
- };
124
- }
125
- /**
126
- * ドキュメントパスからIDを抽出
127
- */
128
- function getDocumentId(path) {
129
- if (!path)
130
- return "";
131
- const pathParts = path.split("/");
132
- return pathParts[pathParts.length - 1];
133
- }
134
- // JWT(JSON Web Token)を作成する関数
135
- async function createJWT(serviceAccount) {
136
- const now = Math.floor(Date.now() / 1000);
137
- const payload = {
138
- iss: serviceAccount.client_email,
139
- sub: serviceAccount.client_email,
140
- aud: "https://oauth2.googleapis.com/token",
141
- iat: now,
142
- exp: now + 3600, // 1時間後に期限切れ
143
- scope: "https://www.googleapis.com/auth/datastore",
144
- };
145
- try {
146
- // 秘密鍵をインポート
147
- const privateKey = await jose.importPKCS8(serviceAccount.private_key, "RS256");
148
- // JWTを作成
149
- const token = await new jose.SignJWT(payload)
150
- .setProtectedHeader({
151
- alg: "RS256",
152
- kid: serviceAccount.private_key_id,
153
- typ: "JWT",
154
- })
155
- .sign(privateKey);
156
- return token;
157
- }
158
- catch (error) {
159
- console.error("JWT作成エラー:", error);
160
- throw error;
161
- }
162
- }
163
- /**
164
- * Firestoreの認証トークンを取得する関数
165
- */
166
- export async function getFirestoreToken() {
167
- // トークンを取得するためのリクエスト
168
- const response = await fetch("https://oauth2.googleapis.com/token", {
169
- method: "POST",
170
- headers: {
171
- "Content-Type": "application/json",
172
- },
173
- body: JSON.stringify({
174
- grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
175
- assertion: await createJWT(serviceAccount),
176
- }),
177
- });
178
- const data = (await response.json());
179
- return data.access_token;
180
- }
181
- /**
182
- * Firestoreクライアントクラス
183
- */
184
- export class FirestoreClient {
185
- constructor() {
186
- this.token = null;
187
- this.tokenExpiry = 0;
188
- }
189
- /**
190
- * 認証トークンを取得(キャッシュあり)
191
- */
192
- async getToken() {
193
- const now = Date.now();
194
- // トークンが期限切れか未取得の場合は新しく取得
195
- if (!this.token || now >= this.tokenExpiry) {
196
- this.token = await getFirestoreToken();
197
- // 50分後に期限切れとする(実際は1時間)
198
- this.tokenExpiry = now + 50 * 60 * 1000;
199
- }
200
- return this.token;
201
- }
202
- /**
203
- * Firestoreにドキュメントを作成
204
- */
205
- async create(collectionName, data) {
206
- const url = getFirestoreBasePath(collectionName);
207
- const firestoreData = convertToFirestoreDocument(data);
208
- const token = await this.getToken();
209
- const response = await fetch(url, {
210
- method: "POST",
211
- headers: {
212
- "Content-Type": "application/json",
213
- Authorization: `Bearer ${token}`,
214
- },
215
- body: JSON.stringify(firestoreData),
216
- });
217
- if (!response.ok) {
218
- throw new Error(`Firestore API error: ${response.statusText}`);
219
- }
220
- const result = (await response.json());
221
- return convertFromFirestoreDocument(result);
222
- }
223
- /**
224
- * ドキュメントを取得
225
- */
226
- async get(collectionName, documentId) {
227
- const url = `${getFirestoreBasePath(collectionName)}/${documentId}`;
228
- const token = await this.getToken();
229
- const response = await fetch(url, {
230
- method: "GET",
231
- headers: {
232
- Authorization: `Bearer ${token}`,
233
- },
234
- });
235
- if (response.status === 404) {
236
- return null;
237
- }
238
- if (!response.ok) {
239
- throw new Error(`Firestore API error: ${response.statusText}`);
240
- }
241
- const result = (await response.json());
242
- return convertFromFirestoreDocument(result);
243
- }
244
- /**
245
- * ドキュメントを更新
246
- */
247
- async update(collectionName, documentId, data) {
248
- const url = `${getFirestoreBasePath(collectionName)}/${documentId}`;
249
- const firestoreData = convertToFirestoreDocument(data);
250
- const token = await this.getToken();
251
- const response = await fetch(url, {
252
- method: "PATCH",
253
- headers: {
254
- "Content-Type": "application/json",
255
- Authorization: `Bearer ${token}`,
256
- },
257
- body: JSON.stringify(firestoreData),
258
- });
259
- if (!response.ok) {
260
- throw new Error(`Firestore API error: ${response.statusText}`);
261
- }
262
- const result = (await response.json());
263
- return convertFromFirestoreDocument(result);
264
- }
265
- /**
266
- * ドキュメントを削除
267
- */
268
- async delete(collectionName, documentId) {
269
- const url = `${getFirestoreBasePath(collectionName)}/${documentId}`;
270
- const token = await this.getToken();
271
- const response = await fetch(url, {
272
- method: "DELETE",
273
- headers: {
274
- Authorization: `Bearer ${token}`,
275
- },
276
- });
277
- if (!response.ok) {
278
- throw new Error(`Firestore API error: ${response.statusText}`);
279
- }
280
- return true;
281
- }
282
- /**
283
- * コレクションのドキュメントを検索
284
- */
285
- async query(collectionName, options = {}) {
286
- const url = `${getFirestoreBasePath(collectionName)}:runQuery`;
287
- // クエリ構築
288
- const structuredQuery = {
289
- from: [{ collectionId: collectionName }],
290
- };
291
- // フィルター条件
292
- if (options.where && options.where.length > 0) {
293
- structuredQuery.where = {
294
- compositeFilter: {
295
- op: "AND",
296
- filters: options.where.map(condition => ({
297
- fieldFilter: {
298
- field: { fieldPath: condition.field },
299
- op: condition.op,
300
- value: convertToFirestoreValue(condition.value),
301
- },
302
- })),
303
- },
304
- };
305
- }
306
- // 並べ替え
307
- if (options.orderBy) {
308
- structuredQuery.orderBy = [
309
- {
310
- field: { fieldPath: options.orderBy },
311
- direction: "ASCENDING",
312
- },
313
- ];
314
- }
315
- // 制限
316
- if (options.limit) {
317
- structuredQuery.limit = options.limit;
318
- }
319
- // オフセット
320
- if (options.offset) {
321
- structuredQuery.offset = options.offset;
322
- }
323
- const token = await this.getToken();
324
- const response = await fetch(url, {
325
- method: "POST",
326
- headers: {
327
- "Content-Type": "application/json",
328
- Authorization: `Bearer ${token}`,
329
- },
330
- body: JSON.stringify({
331
- structuredQuery,
332
- }),
333
- });
334
- if (!response.ok) {
335
- throw new Error(`Firestore API error: ${response.statusText}`);
336
- }
337
- const results = (await response.json());
338
- return results
339
- .filter(item => item.document)
340
- .map(item => convertFromFirestoreDocument(item.document));
341
- }
342
- }
343
- // デフォルトのFirestoreクライアントインスタンス
344
- export const firestore = new FirestoreClient();
345
- // 後方互換性のための関数
346
- export async function saveToFirestore(collectionName, data) {
347
- const result = await firestore.create(collectionName, data);
348
- return { documentId: result.id, result };
349
- }