firebase-rest-firestore 1.2.0 → 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.
Files changed (52) hide show
  1. package/README.md +37 -0
  2. package/dist/cjs/client.d.ts +454 -0
  3. package/dist/cjs/client.js +1192 -0
  4. package/dist/cjs/field-value.d.ts +22 -0
  5. package/dist/cjs/field-value.js +31 -0
  6. package/dist/{index.d.ts → cjs/index.d.ts} +2 -1
  7. package/dist/{index.js → cjs/index.js} +4 -2
  8. package/dist/cjs/types.d.ts +146 -0
  9. package/dist/cjs/types.js +75 -0
  10. package/dist/cjs/utils/auth.d.ts +13 -0
  11. package/dist/{utils → cjs/utils}/auth.js +14 -10
  12. package/dist/cjs/utils/config.d.ts +6 -0
  13. package/dist/{utils → cjs/utils}/config.js +0 -12
  14. package/dist/cjs/utils/converter.d.ts +57 -0
  15. package/dist/cjs/utils/converter.js +224 -0
  16. package/dist/cjs/utils/path.d.ts +73 -0
  17. package/dist/cjs/utils/path.js +176 -0
  18. package/dist/esm/client.d.ts +454 -0
  19. package/dist/esm/client.js +1180 -0
  20. package/dist/esm/field-value.d.ts +22 -0
  21. package/dist/esm/field-value.js +27 -0
  22. package/dist/esm/index.d.ts +8 -0
  23. package/dist/esm/index.js +13 -0
  24. package/dist/esm/types.d.ts +146 -0
  25. package/dist/esm/types.js +70 -0
  26. package/dist/esm/utils/auth.d.ts +13 -0
  27. package/dist/esm/utils/auth.js +57 -0
  28. package/dist/esm/utils/config.d.ts +6 -0
  29. package/dist/esm/utils/config.js +11 -0
  30. package/dist/esm/utils/converter.d.ts +57 -0
  31. package/dist/esm/utils/converter.js +216 -0
  32. package/dist/esm/utils/path.d.ts +73 -0
  33. package/dist/esm/utils/path.js +169 -0
  34. package/dist/types/client.d.ts +454 -0
  35. package/dist/types/field-value.d.ts +22 -0
  36. package/dist/types/index.d.ts +8 -0
  37. package/dist/types/types.d.ts +146 -0
  38. package/dist/types/utils/auth.d.ts +13 -0
  39. package/dist/types/utils/config.d.ts +6 -0
  40. package/dist/types/utils/converter.d.ts +57 -0
  41. package/dist/types/utils/path.d.ts +73 -0
  42. package/package.json +19 -5
  43. package/dist/client.d.ts +0 -381
  44. package/dist/client.js +0 -915
  45. package/dist/types.d.ts +0 -65
  46. package/dist/types.js +0 -2
  47. package/dist/utils/auth.d.ts +0 -13
  48. package/dist/utils/config.d.ts +0 -13
  49. package/dist/utils/converter.d.ts +0 -27
  50. package/dist/utils/converter.js +0 -113
  51. package/dist/utils/path.d.ts +0 -14
  52. package/dist/utils/path.js +0 -27
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertToFirestoreValue = convertToFirestoreValue;
4
+ exports.convertFromFirestoreValue = convertFromFirestoreValue;
5
+ exports.convertToFirestoreDocument = convertToFirestoreDocument;
6
+ exports.extractFieldTransforms = extractFieldTransforms;
7
+ exports.buildCommitWrite = buildCommitWrite;
8
+ exports.convertFromFirestoreDocument = convertFromFirestoreDocument;
9
+ const client_1 = require("../client");
10
+ const field_value_1 = require("../field-value");
11
+ const types_1 = require("../types");
12
+ const path_1 = require("./path");
13
+ /**
14
+ * JSの値をFirestore形式に変換する
15
+ * @param value 変換する値
16
+ * @returns Firestore形式の値
17
+ */
18
+ function convertToFirestoreValue(value) {
19
+ if (value instanceof field_value_1.FieldValue) {
20
+ // Sentinels (e.g. serverTimestamp) must be extracted into field transforms
21
+ // before conversion. Reaching here means one was used where Firestore
22
+ // cannot express a transform (such as inside an array).
23
+ throw new Error("FieldValue (e.g. serverTimestamp()) can only be used as a top-level or nested document field value, not inside an array.");
24
+ }
25
+ if (value instanceof Date) {
26
+ return { timestampValue: value.toISOString() };
27
+ }
28
+ else if (value instanceof client_1.DocumentReference) {
29
+ return { referenceValue: value.referenceValue };
30
+ }
31
+ else if (value instanceof types_1.LiteralDocumentReference) {
32
+ return { referenceValue: value.referenceValue };
33
+ }
34
+ else if (value instanceof types_1.LiteralGeoPointValue) {
35
+ return { geoPointValue: value.geoPointValue };
36
+ }
37
+ else if (typeof value === "string") {
38
+ return { stringValue: value };
39
+ }
40
+ else if (typeof value === "number") {
41
+ return Number.isInteger(value)
42
+ ? { integerValue: value }
43
+ : { doubleValue: value };
44
+ }
45
+ else if (typeof value === "boolean") {
46
+ return { booleanValue: value };
47
+ }
48
+ else if (value === null || value === undefined) {
49
+ return { nullValue: null };
50
+ }
51
+ else if (Array.isArray(value)) {
52
+ return {
53
+ arrayValue: {
54
+ values: value.map(item => convertToFirestoreValue(item)),
55
+ },
56
+ };
57
+ }
58
+ else if (typeof value === "object") {
59
+ const fields = Object.entries(value).reduce((acc, [key, val]) => ({
60
+ ...acc,
61
+ [key]: convertToFirestoreValue(val),
62
+ }), {});
63
+ return { mapValue: { fields } };
64
+ }
65
+ // デフォルトは文字列化
66
+ return { stringValue: String(value) };
67
+ }
68
+ /**
69
+ * Firestore形式からJSの値に変換する
70
+ * @param firestoreValue Firestore形式の値
71
+ * @returns JS形式の値
72
+ */
73
+ function convertFromFirestoreValue(firestoreValue) {
74
+ if ("stringValue" in firestoreValue) {
75
+ return firestoreValue.stringValue;
76
+ }
77
+ else if ("integerValue" in firestoreValue) {
78
+ return Number(firestoreValue.integerValue);
79
+ }
80
+ else if ("doubleValue" in firestoreValue) {
81
+ return firestoreValue.doubleValue;
82
+ }
83
+ else if ("booleanValue" in firestoreValue) {
84
+ return firestoreValue.booleanValue;
85
+ }
86
+ else if ("nullValue" in firestoreValue) {
87
+ return null;
88
+ }
89
+ else if ("timestampValue" in firestoreValue) {
90
+ return new Date(firestoreValue.timestampValue);
91
+ }
92
+ else if ("geoPointValue" in firestoreValue) {
93
+ return new types_1.LiteralGeoPointValue(firestoreValue);
94
+ }
95
+ else if ("referenceValue" in firestoreValue) {
96
+ return new types_1.LiteralDocumentReference(firestoreValue);
97
+ }
98
+ else if ("mapValue" in firestoreValue && firestoreValue.mapValue.fields) {
99
+ return Object.entries(firestoreValue.mapValue.fields).reduce((acc, [key, val]) => ({
100
+ ...acc,
101
+ [key]: convertFromFirestoreValue(val),
102
+ }), {});
103
+ }
104
+ else if ("arrayValue" in firestoreValue) {
105
+ // The `values` field can be undefined, meaning that this is an empty array
106
+ return (firestoreValue.arrayValue.values ?? []).map(convertFromFirestoreValue);
107
+ }
108
+ return null;
109
+ }
110
+ /**
111
+ * オブジェクトをFirestoreドキュメント形式に変換
112
+ * @param data 変換するオブジェクト
113
+ * @returns Firestoreドキュメント
114
+ */
115
+ function convertToFirestoreDocument(data) {
116
+ return {
117
+ fields: Object.entries(data).reduce((acc, [key, value]) => ({
118
+ ...acc,
119
+ [key]: convertToFirestoreValue(value),
120
+ }), {}),
121
+ };
122
+ }
123
+ /**
124
+ * Whether a value is a plain JS object (`{}` / `Object.create(null)`), as
125
+ * opposed to a class instance such as `Date`, `DocumentReference`,
126
+ * `LiteralGeoPointValue`, `LiteralDocumentReference`, `FieldValue`, or an array.
127
+ * Only plain objects are recursed into when extracting field transforms.
128
+ */
129
+ function isPlainObject(value) {
130
+ if (value === null || typeof value !== "object")
131
+ return false;
132
+ const proto = Object.getPrototypeOf(value);
133
+ return proto === Object.prototype || proto === null;
134
+ }
135
+ /**
136
+ * Escape a single field name for use in a Firestore field path. Simple names
137
+ * (`[A-Za-z_][A-Za-z0-9_]*`) are used as-is; anything else (dashes, dots,
138
+ * leading digits, spaces, ...) is wrapped in backticks with `\` and `` ` ``
139
+ * escaped, so a literal dot in a key is treated as part of the name rather than
140
+ * a path separator.
141
+ * See: https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents#Document
142
+ */
143
+ function escapeFieldPathSegment(segment) {
144
+ if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(segment)) {
145
+ return segment;
146
+ }
147
+ return "`" + segment.replace(/\\/g, "\\\\").replace(/`/g, "\\`") + "`";
148
+ }
149
+ /**
150
+ * Split write data into plain field values and Firestore field transforms.
151
+ *
152
+ * `FieldValue` sentinels (e.g. `serverTimestamp()`) are pulled out into
153
+ * transforms keyed by their (escaped, dot-separated) field path; everything
154
+ * else is left untouched in `fields`. Recursion only descends into plain
155
+ * objects, so class instances (Date / references / geo points) are treated as
156
+ * leaves.
157
+ *
158
+ * @param data Write data (JS values, may contain FieldValue sentinels)
159
+ * @param prefix Field-path prefix used while recursing (internal, pre-escaped)
160
+ */
161
+ function extractFieldTransforms(data, prefix = "") {
162
+ const fields = {};
163
+ const transforms = [];
164
+ for (const [key, value] of Object.entries(data)) {
165
+ const escapedKey = escapeFieldPathSegment(key);
166
+ const fieldPath = prefix ? `${prefix}.${escapedKey}` : escapedKey;
167
+ if (value instanceof field_value_1.FieldValue) {
168
+ if (value.methodName === "serverTimestamp") {
169
+ transforms.push({ fieldPath, setToServerValue: "REQUEST_TIME" });
170
+ }
171
+ else {
172
+ throw new Error(`Unsupported FieldValue: ${value.methodName}`);
173
+ }
174
+ }
175
+ else if (isPlainObject(value)) {
176
+ const nested = extractFieldTransforms(value, fieldPath);
177
+ fields[key] = nested.fields;
178
+ transforms.push(...nested.transforms);
179
+ }
180
+ else {
181
+ fields[key] = value;
182
+ }
183
+ }
184
+ return { fields, transforms };
185
+ }
186
+ /**
187
+ * Build a single `documents:commit` write that updates a document and applies
188
+ * field transforms. `updateTransforms` / `currentDocument` are only included
189
+ * when relevant.
190
+ *
191
+ * @param documentName Full resource name (projects/.../documents/<path>)
192
+ * @param fields Already-converted Firestore field values
193
+ * @param transforms Field transforms to apply after the update
194
+ * @param currentDocument Optional precondition (e.g. `{ exists: false }`)
195
+ */
196
+ function buildCommitWrite(documentName, fields, transforms, currentDocument) {
197
+ const write = {
198
+ update: { name: documentName, fields },
199
+ };
200
+ if (transforms.length > 0) {
201
+ write.updateTransforms = transforms;
202
+ }
203
+ if (currentDocument) {
204
+ write.currentDocument = currentDocument;
205
+ }
206
+ return write;
207
+ }
208
+ /**
209
+ * Firestoreドキュメントをオブジェクトに変換
210
+ * @param doc Firestoreレスポンス
211
+ * @returns 変換されたオブジェクト(idプロパティ付き)
212
+ */
213
+ function convertFromFirestoreDocument(doc) {
214
+ if (!doc.fields)
215
+ return { id: (0, path_1.getDocumentId)(doc.name) };
216
+ const result = Object.entries(doc.fields).reduce((acc, [key, value]) => ({
217
+ ...acc,
218
+ [key]: convertFromFirestoreValue(value),
219
+ }), {});
220
+ return {
221
+ ...result,
222
+ id: (0, path_1.getDocumentId)(doc.name),
223
+ };
224
+ }
@@ -0,0 +1,73 @@
1
+ import { FirestoreConfig } from "../types";
2
+ /**
3
+ * Utility class for constructing Firestore URIs
4
+ * Consistently handles different types of paths and operations
5
+ */
6
+ export declare class FirestorePath {
7
+ private projectId;
8
+ private databaseId;
9
+ private useEmulator;
10
+ private emulatorHost;
11
+ private emulatorPort;
12
+ private debug;
13
+ /**
14
+ * Constructor
15
+ */
16
+ constructor(config: FirestoreConfig, debug?: boolean);
17
+ /**
18
+ * Get Firestore base URL (without document path)
19
+ */
20
+ getBasePath(): string;
21
+ /**
22
+ * Get base URL + collection path for a collection root
23
+ * @param path Collection path (ex: "users" or "users/uid/posts")
24
+ */
25
+ getCollectionPath(path: string): string;
26
+ /**
27
+ * Get the complete URL for a document
28
+ * @param collectionPath Collection path
29
+ * @param documentId Document ID
30
+ */
31
+ getDocumentPath(collectionPath: string, documentId: string): string;
32
+ /**
33
+ * Get URL for query execution
34
+ * @param path Collection path (ex: "users" or "users/uid/posts")
35
+ * @returns URL for query execution, collection ID, and parent path (if needed)
36
+ */
37
+ getQueryPath(path: string): {
38
+ url: string;
39
+ collectionId: string;
40
+ parentPath?: string;
41
+ };
42
+ /**
43
+ * Get reference path for parent document (for query construction)
44
+ * @param parentPath Parent document path
45
+ */
46
+ getParentReference(parentPath: string): string;
47
+ /**
48
+ * Get URL for runQuery
49
+ * @param collectionPath Collection path
50
+ * @returns URL for executing runQuery
51
+ */
52
+ getRunQueryPath(collectionPath: string): string;
53
+ }
54
+ /**
55
+ * Create an instance of FirestorePath class
56
+ * @param config Firestore configuration
57
+ * @param debug Debug mode
58
+ */
59
+ export declare function createFirestorePath(config: FirestoreConfig, debug?: boolean): FirestorePath;
60
+ /**
61
+ * Get Firestore base path URL (without path)
62
+ * @param projectId Project ID
63
+ * @param databaseId Database ID (defaults to default)
64
+ * @param config Firestore configuration (for emulator settings)
65
+ * @returns Firestore base path URL (without path)
66
+ */
67
+ export declare function getFirestoreBasePath(projectId: string, databaseId?: string, config?: FirestoreConfig): string;
68
+ /**
69
+ * Extract document ID from document path
70
+ * @param path Document path
71
+ * @returns Document ID
72
+ */
73
+ export declare function getDocumentId(path: string): string;
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FirestorePath = void 0;
4
+ exports.createFirestorePath = createFirestorePath;
5
+ exports.getFirestoreBasePath = getFirestoreBasePath;
6
+ exports.getDocumentId = getDocumentId;
7
+ /**
8
+ * Utility class for constructing Firestore URIs
9
+ * Consistently handles different types of paths and operations
10
+ */
11
+ class FirestorePath {
12
+ /**
13
+ * Constructor
14
+ */
15
+ constructor(config, debug = false) {
16
+ this.useEmulator = false;
17
+ this.emulatorHost = "localhost";
18
+ this.emulatorPort = 8080;
19
+ this.debug = false;
20
+ this.projectId = config.projectId;
21
+ this.databaseId = config.databaseId || "(default)";
22
+ this.debug = debug;
23
+ if (config.useEmulator) {
24
+ this.useEmulator = true;
25
+ this.emulatorHost = config.emulatorHost || "localhost";
26
+ this.emulatorPort = config.emulatorPort || 8080;
27
+ }
28
+ }
29
+ /**
30
+ * Get Firestore base URL (without document path)
31
+ */
32
+ getBasePath() {
33
+ const baseUrl = this.useEmulator
34
+ ? `http://${this.emulatorHost}:${this.emulatorPort}/v1`
35
+ : "https://firestore.googleapis.com/v1";
36
+ const path = `${baseUrl}/projects/${this.projectId}/databases/${this.databaseId}/documents`;
37
+ if (this.debug) {
38
+ console.log(`Generated base path: ${path}`);
39
+ }
40
+ return path;
41
+ }
42
+ /**
43
+ * Get base URL + collection path for a collection root
44
+ * @param path Collection path (ex: "users" or "users/uid/posts")
45
+ */
46
+ getCollectionPath(path) {
47
+ // Remove leading and trailing slashes
48
+ const cleanPath = path.replace(/^\/+|\/+$/g, '');
49
+ const fullPath = `${this.getBasePath()}/${cleanPath}`;
50
+ if (this.debug) {
51
+ console.log(`Generated collection path: ${fullPath}`);
52
+ }
53
+ return fullPath;
54
+ }
55
+ /**
56
+ * Get the complete URL for a document
57
+ * @param collectionPath Collection path
58
+ * @param documentId Document ID
59
+ */
60
+ getDocumentPath(collectionPath, documentId) {
61
+ const cleanCollectionPath = collectionPath.replace(/^\/+|\/+$/g, '');
62
+ const path = `${this.getBasePath()}/${cleanCollectionPath}/${documentId}`;
63
+ if (this.debug) {
64
+ console.log(`Generated document path: ${path}`);
65
+ }
66
+ return path;
67
+ }
68
+ /**
69
+ * Get URL for query execution
70
+ * @param path Collection path (ex: "users" or "users/uid/posts")
71
+ * @returns URL for query execution, collection ID, and parent path (if needed)
72
+ */
73
+ getQueryPath(path) {
74
+ // パスをセグメントに分割
75
+ const segments = path.replace(/^\/+|\/+$/g, '').split('/');
76
+ // 単一コレクションの場合
77
+ if (segments.length === 1) {
78
+ const url = `${this.getBasePath()}:runQuery`;
79
+ if (this.debug) {
80
+ console.log(`Generated query URL (single collection): ${url}`);
81
+ console.log(`Collection ID: ${segments[0]}`);
82
+ }
83
+ return {
84
+ url,
85
+ collectionId: segments[0]
86
+ };
87
+ }
88
+ // ネストしたコレクションパスの場合 (例: "users/uid/posts")
89
+ const collectionId = segments[segments.length - 1];
90
+ const parentSegments = segments.slice(0, -1);
91
+ const parentPath = parentSegments.join('/');
92
+ // ベースURLでネストしたドキュメントまでのパスを取得
93
+ const url = `${this.getBasePath()}:runQuery`;
94
+ if (this.debug) {
95
+ console.log(`Generated query URL (nested collection): ${url}`);
96
+ console.log(`Collection ID: ${collectionId}`);
97
+ console.log(`Parent path: ${parentPath}`);
98
+ }
99
+ return {
100
+ url,
101
+ collectionId,
102
+ parentPath
103
+ };
104
+ }
105
+ /**
106
+ * Get reference path for parent document (for query construction)
107
+ * @param parentPath Parent document path
108
+ */
109
+ getParentReference(parentPath) {
110
+ return `projects/${this.projectId}/databases/${this.databaseId}/documents/${parentPath}`;
111
+ }
112
+ /**
113
+ * Get URL for runQuery
114
+ * @param collectionPath Collection path
115
+ * @returns URL for executing runQuery
116
+ */
117
+ getRunQueryPath(collectionPath) {
118
+ // コレクションパス情報を取得
119
+ const { collectionId, parentPath } = this.getQueryPath(collectionPath);
120
+ // parentPathがある場合は、親ドキュメントパスを使用してURLを作成
121
+ if (parentPath) {
122
+ // getBasePathからベースURLを取得
123
+ const baseUrl = this.getBasePath().replace(/\/documents$/, '');
124
+ // 親ドキュメントパスを含むrunQueryのURL
125
+ const runQueryUrl = `${baseUrl}/documents/${parentPath}:runQuery`;
126
+ if (this.debug) {
127
+ console.log(`Generated runQuery URL for nested collection: ${runQueryUrl}`);
128
+ console.log(`Collection ID: ${collectionId}`);
129
+ }
130
+ return runQueryUrl;
131
+ }
132
+ // トップレベルコレクションの場合は、ベースパスを使用
133
+ const baseUrl = this.getBasePath();
134
+ const runQueryUrl = `${baseUrl}:runQuery`;
135
+ if (this.debug) {
136
+ console.log(`Generated runQuery URL for top-level collection: ${runQueryUrl}`);
137
+ console.log(`Collection ID: ${collectionId}`);
138
+ }
139
+ return runQueryUrl;
140
+ }
141
+ }
142
+ exports.FirestorePath = FirestorePath;
143
+ /**
144
+ * Create an instance of FirestorePath class
145
+ * @param config Firestore configuration
146
+ * @param debug Debug mode
147
+ */
148
+ function createFirestorePath(config, debug = false) {
149
+ return new FirestorePath(config, debug);
150
+ }
151
+ /**
152
+ * Get Firestore base path URL (without path)
153
+ * @param projectId Project ID
154
+ * @param databaseId Database ID (defaults to default)
155
+ * @param config Firestore configuration (for emulator settings)
156
+ * @returns Firestore base path URL (without path)
157
+ */
158
+ function getFirestoreBasePath(projectId, databaseId, config) {
159
+ // Use emulator URL for emulator mode
160
+ if (config?.useEmulator) {
161
+ const host = config.emulatorHost || "127.0.0.1";
162
+ const port = config.emulatorPort || 8080;
163
+ return `http://${host}:${port}/v1/projects/${projectId}/databases/${databaseId || "(default)"}/documents`;
164
+ }
165
+ // Use normal production environment URL
166
+ return `https://firestore.googleapis.com/v1/projects/${projectId}/databases/${databaseId || "(default)"}/documents`;
167
+ }
168
+ /**
169
+ * Extract document ID from document path
170
+ * @param path Document path
171
+ * @returns Document ID
172
+ */
173
+ function getDocumentId(path) {
174
+ const parts = path.split("/");
175
+ return parts[parts.length - 1];
176
+ }