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.
- package/README.md +37 -0
- package/dist/cjs/client.d.ts +454 -0
- package/dist/cjs/client.js +1192 -0
- package/dist/cjs/field-value.d.ts +22 -0
- package/dist/cjs/field-value.js +31 -0
- package/dist/{index.d.ts → cjs/index.d.ts} +2 -1
- package/dist/{index.js → cjs/index.js} +4 -2
- package/dist/cjs/types.d.ts +146 -0
- package/dist/cjs/types.js +75 -0
- package/dist/cjs/utils/auth.d.ts +13 -0
- package/dist/{utils → cjs/utils}/auth.js +14 -10
- package/dist/cjs/utils/config.d.ts +6 -0
- package/dist/{utils → cjs/utils}/config.js +0 -12
- package/dist/cjs/utils/converter.d.ts +57 -0
- package/dist/cjs/utils/converter.js +224 -0
- package/dist/cjs/utils/path.d.ts +73 -0
- package/dist/cjs/utils/path.js +176 -0
- package/dist/esm/client.d.ts +454 -0
- package/dist/esm/client.js +1180 -0
- package/dist/esm/field-value.d.ts +22 -0
- package/dist/esm/field-value.js +27 -0
- package/dist/esm/index.d.ts +8 -0
- package/dist/esm/index.js +13 -0
- package/dist/esm/types.d.ts +146 -0
- package/dist/esm/types.js +70 -0
- package/dist/esm/utils/auth.d.ts +13 -0
- package/dist/esm/utils/auth.js +57 -0
- package/dist/esm/utils/config.d.ts +6 -0
- package/dist/esm/utils/config.js +11 -0
- package/dist/esm/utils/converter.d.ts +57 -0
- package/dist/esm/utils/converter.js +216 -0
- package/dist/esm/utils/path.d.ts +73 -0
- package/dist/esm/utils/path.js +169 -0
- package/dist/types/client.d.ts +454 -0
- package/dist/types/field-value.d.ts +22 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/types.d.ts +146 -0
- package/dist/types/utils/auth.d.ts +13 -0
- package/dist/types/utils/config.d.ts +6 -0
- package/dist/types/utils/converter.d.ts +57 -0
- package/dist/types/utils/path.d.ts +73 -0
- package/package.json +19 -5
- package/dist/client.d.ts +0 -381
- package/dist/client.js +0 -915
- package/dist/types.d.ts +0 -65
- package/dist/types.js +0 -2
- package/dist/utils/auth.d.ts +0 -13
- package/dist/utils/config.d.ts +0 -13
- package/dist/utils/converter.d.ts +0 -27
- package/dist/utils/converter.js +0 -113
- package/dist/utils/path.d.ts +0 -14
- package/dist/utils/path.js +0 -27
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel values for special write behaviors, mirroring the native Firebase
|
|
3
|
+
* SDK's `FieldValue`. Currently only `serverTimestamp()` is supported.
|
|
4
|
+
*
|
|
5
|
+
* A `FieldValue` is not a real field value: when used as a field in a write it
|
|
6
|
+
* is translated into a Firestore field transform (applied server-side) rather
|
|
7
|
+
* than serialized as data. Using one anywhere else (e.g. inside an array) is an
|
|
8
|
+
* error.
|
|
9
|
+
*/
|
|
10
|
+
export declare class FieldValue {
|
|
11
|
+
readonly methodName: "serverTimestamp";
|
|
12
|
+
private constructor();
|
|
13
|
+
/**
|
|
14
|
+
* Returns a sentinel that sets the field to the server's request timestamp at
|
|
15
|
+
* write time, e.g. `client.add("posts", { createdAt: FieldValue.serverTimestamp() })`.
|
|
16
|
+
*/
|
|
17
|
+
static serverTimestamp(): FieldValue;
|
|
18
|
+
/**
|
|
19
|
+
* Whether this sentinel represents the same transform as another.
|
|
20
|
+
*/
|
|
21
|
+
isEqual(other: FieldValue): boolean;
|
|
22
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel values for special write behaviors, mirroring the native Firebase
|
|
3
|
+
* SDK's `FieldValue`. Currently only `serverTimestamp()` is supported.
|
|
4
|
+
*
|
|
5
|
+
* A `FieldValue` is not a real field value: when used as a field in a write it
|
|
6
|
+
* is translated into a Firestore field transform (applied server-side) rather
|
|
7
|
+
* than serialized as data. Using one anywhere else (e.g. inside an array) is an
|
|
8
|
+
* error.
|
|
9
|
+
*/
|
|
10
|
+
export class FieldValue {
|
|
11
|
+
constructor(methodName) {
|
|
12
|
+
this.methodName = methodName;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Returns a sentinel that sets the field to the server's request timestamp at
|
|
16
|
+
* write time, e.g. `client.add("posts", { createdAt: FieldValue.serverTimestamp() })`.
|
|
17
|
+
*/
|
|
18
|
+
static serverTimestamp() {
|
|
19
|
+
return new FieldValue("serverTimestamp");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Whether this sentinel represents the same transform as another.
|
|
23
|
+
*/
|
|
24
|
+
isEqual(other) {
|
|
25
|
+
return other instanceof FieldValue && other.methodName === this.methodName;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./types";
|
|
2
|
+
import { FirestoreClient, createFirestoreClient, CollectionReference, DocumentReference, CollectionGroup, Query, QuerySnapshot, DocumentSnapshot, WriteResult } from "./client";
|
|
3
|
+
export { FieldValue } from "./field-value";
|
|
4
|
+
export { getFirestoreToken } from "./utils/auth";
|
|
5
|
+
export { convertToFirestoreValue, convertFromFirestoreValue, convertToFirestoreDocument, convertFromFirestoreDocument, } from "./utils/converter";
|
|
6
|
+
export { getFirestoreBasePath, getDocumentId } from "./utils/path";
|
|
7
|
+
export { formatPrivateKey } from "./utils/config";
|
|
8
|
+
export { FirestoreClient, createFirestoreClient, CollectionReference, DocumentReference, CollectionGroup, Query, QuerySnapshot, DocumentSnapshot, WriteResult, };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// 型定義のエクスポート
|
|
2
|
+
export * from "./types";
|
|
3
|
+
// クライアントのエクスポート
|
|
4
|
+
import { FirestoreClient, createFirestoreClient, CollectionReference, DocumentReference, CollectionGroup, Query, QuerySnapshot, DocumentSnapshot, WriteResult, } from "./client";
|
|
5
|
+
// FieldValue センチネルのエクスポート
|
|
6
|
+
export { FieldValue } from "./field-value";
|
|
7
|
+
// ユーティリティ関数のエクスポート
|
|
8
|
+
export { getFirestoreToken } from "./utils/auth";
|
|
9
|
+
export { convertToFirestoreValue, convertFromFirestoreValue, convertToFirestoreDocument, convertFromFirestoreDocument, } from "./utils/converter";
|
|
10
|
+
export { getFirestoreBasePath, getDocumentId } from "./utils/path";
|
|
11
|
+
export { formatPrivateKey } from "./utils/config";
|
|
12
|
+
// クライアント関連のエクスポート
|
|
13
|
+
export { FirestoreClient, createFirestoreClient, CollectionReference, DocumentReference, CollectionGroup, Query, QuerySnapshot, DocumentSnapshot, WriteResult, };
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firestoreクライアントの設定インターフェース
|
|
3
|
+
*/
|
|
4
|
+
export interface FirestoreConfig {
|
|
5
|
+
projectId: string;
|
|
6
|
+
privateKey: string;
|
|
7
|
+
clientEmail: string;
|
|
8
|
+
databaseId?: string;
|
|
9
|
+
debug?: boolean;
|
|
10
|
+
useEmulator?: boolean;
|
|
11
|
+
emulatorHost?: string;
|
|
12
|
+
emulatorPort?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A reference to a document. For example: `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
|
|
16
|
+
* Used to represent document references globally and not connected to any particular client.
|
|
17
|
+
*/
|
|
18
|
+
export declare class LiteralDocumentReference {
|
|
19
|
+
referenceValue: string;
|
|
20
|
+
constructor(options: Pick<LiteralDocumentReference, "referenceValue">);
|
|
21
|
+
/**
|
|
22
|
+
* Globally unique Firestore document reference paths look like:
|
|
23
|
+
* projects/{project_id}/databases/{database_id}/documents/{document_path}
|
|
24
|
+
* The database id (e.g. `(default)`) never contains a slash, while the
|
|
25
|
+
* document path may contain many. A single anchored regex parses this
|
|
26
|
+
* without pulling in a URLPattern polyfill.
|
|
27
|
+
*/
|
|
28
|
+
private static readonly pattern;
|
|
29
|
+
private parse;
|
|
30
|
+
/**
|
|
31
|
+
* Get Project ID
|
|
32
|
+
*/
|
|
33
|
+
get project_id(): string;
|
|
34
|
+
/**
|
|
35
|
+
* Get Database ID
|
|
36
|
+
* Ex: `(default)`
|
|
37
|
+
*/
|
|
38
|
+
get database_id(): string;
|
|
39
|
+
/**
|
|
40
|
+
* Get document ID
|
|
41
|
+
*/
|
|
42
|
+
get id(): string;
|
|
43
|
+
/**
|
|
44
|
+
* Get the collection ID
|
|
45
|
+
*/
|
|
46
|
+
get collectionPath(): string;
|
|
47
|
+
/**
|
|
48
|
+
* Get document path
|
|
49
|
+
*/
|
|
50
|
+
get path(): string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A geo point value representing a point on the surface of Earth.
|
|
54
|
+
*/
|
|
55
|
+
export declare class LiteralGeoPointValue {
|
|
56
|
+
geoPointValue: {
|
|
57
|
+
/**
|
|
58
|
+
* The latitude in degrees. It must be in the range [-90.0, +90.0].
|
|
59
|
+
*/
|
|
60
|
+
latitude: number;
|
|
61
|
+
/**
|
|
62
|
+
* The longitude in degrees. It must be in the range [-180.0, +180.0].
|
|
63
|
+
*/
|
|
64
|
+
longitude: number;
|
|
65
|
+
};
|
|
66
|
+
constructor(options: Pick<LiteralGeoPointValue, "geoPointValue">);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Firestoreの値型定義
|
|
70
|
+
* See: https://github.com/googleapis/google-api-nodejs-client/blob/5870dfe31f4885eebc82c19f7471c50403308f26/src/apis/firestore/v1.ts#L2246
|
|
71
|
+
*/
|
|
72
|
+
export type FirestoreFieldValue = {
|
|
73
|
+
stringValue: string;
|
|
74
|
+
} | {
|
|
75
|
+
integerValue: number;
|
|
76
|
+
} | {
|
|
77
|
+
doubleValue: number;
|
|
78
|
+
} | {
|
|
79
|
+
booleanValue: boolean;
|
|
80
|
+
} | {
|
|
81
|
+
nullValue: null;
|
|
82
|
+
} | {
|
|
83
|
+
timestampValue: string;
|
|
84
|
+
} | Pick<LiteralGeoPointValue, 'geoPointValue'> | Pick<LiteralDocumentReference, 'referenceValue'> | {
|
|
85
|
+
mapValue: {
|
|
86
|
+
fields: Record<string, FirestoreFieldValue>;
|
|
87
|
+
};
|
|
88
|
+
} | {
|
|
89
|
+
arrayValue: {
|
|
90
|
+
values: FirestoreFieldValue[];
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* A Firestore field transform applied server-side during a commit write.
|
|
95
|
+
* See: https://firebase.google.com/docs/firestore/reference/rest/v1/Write#FieldTransform
|
|
96
|
+
*/
|
|
97
|
+
export interface FieldTransform {
|
|
98
|
+
fieldPath: string;
|
|
99
|
+
setToServerValue: "REQUEST_TIME";
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A single write in a `documents:commit` request.
|
|
103
|
+
*/
|
|
104
|
+
export interface CommitWrite {
|
|
105
|
+
update: {
|
|
106
|
+
name: string;
|
|
107
|
+
fields: Record<string, FirestoreFieldValue>;
|
|
108
|
+
};
|
|
109
|
+
updateTransforms?: FieldTransform[];
|
|
110
|
+
currentDocument?: {
|
|
111
|
+
exists?: boolean;
|
|
112
|
+
updateTime?: string;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Firestoreドキュメント型
|
|
117
|
+
*/
|
|
118
|
+
export interface FirestoreDocument {
|
|
119
|
+
name?: string;
|
|
120
|
+
fields: Record<string, FirestoreFieldValue>;
|
|
121
|
+
createTime?: string;
|
|
122
|
+
updateTime?: string;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Firestoreレスポンス型
|
|
126
|
+
*/
|
|
127
|
+
export interface FirestoreResponse {
|
|
128
|
+
name: string;
|
|
129
|
+
fields?: Record<string, FirestoreFieldValue>;
|
|
130
|
+
createTime?: string;
|
|
131
|
+
updateTime?: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* クエリオプション型
|
|
135
|
+
*/
|
|
136
|
+
export interface QueryOptions {
|
|
137
|
+
where?: Array<{
|
|
138
|
+
field: string;
|
|
139
|
+
op: string;
|
|
140
|
+
value: any;
|
|
141
|
+
}>;
|
|
142
|
+
orderBy?: string;
|
|
143
|
+
orderDirection?: string;
|
|
144
|
+
limit?: number;
|
|
145
|
+
offset?: number;
|
|
146
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A reference to a document. For example: `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
|
|
3
|
+
* Used to represent document references globally and not connected to any particular client.
|
|
4
|
+
*/
|
|
5
|
+
export class LiteralDocumentReference {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.referenceValue = options.referenceValue;
|
|
8
|
+
}
|
|
9
|
+
parse() {
|
|
10
|
+
const match = LiteralDocumentReference.pattern.exec(this.referenceValue);
|
|
11
|
+
if (!match) {
|
|
12
|
+
throw new Error("Invalid document path. Path does not match pattern.");
|
|
13
|
+
}
|
|
14
|
+
const [, project_id, database_id, document_path] = match;
|
|
15
|
+
return { project_id, database_id, document_path };
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Get Project ID
|
|
19
|
+
*/
|
|
20
|
+
get project_id() {
|
|
21
|
+
return this.parse().project_id;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get Database ID
|
|
25
|
+
* Ex: `(default)`
|
|
26
|
+
*/
|
|
27
|
+
get database_id() {
|
|
28
|
+
return this.parse().database_id;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get document ID
|
|
32
|
+
*/
|
|
33
|
+
get id() {
|
|
34
|
+
const path = this.parse().document_path;
|
|
35
|
+
const parts = path.split("/");
|
|
36
|
+
const docId = parts[parts.length - 1];
|
|
37
|
+
return docId;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get the collection ID
|
|
41
|
+
*/
|
|
42
|
+
get collectionPath() {
|
|
43
|
+
const path = this.parse().document_path;
|
|
44
|
+
const parts = path.split("/");
|
|
45
|
+
const collectionPath = parts.slice(0, parts.length - 1).join("/");
|
|
46
|
+
return collectionPath;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get document path
|
|
50
|
+
*/
|
|
51
|
+
get path() {
|
|
52
|
+
return this.parse().document_path;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Globally unique Firestore document reference paths look like:
|
|
57
|
+
* projects/{project_id}/databases/{database_id}/documents/{document_path}
|
|
58
|
+
* The database id (e.g. `(default)`) never contains a slash, while the
|
|
59
|
+
* document path may contain many. A single anchored regex parses this
|
|
60
|
+
* without pulling in a URLPattern polyfill.
|
|
61
|
+
*/
|
|
62
|
+
LiteralDocumentReference.pattern = /^projects\/([^/]+)\/databases\/([^/]+)\/documents\/(.+)$/;
|
|
63
|
+
/**
|
|
64
|
+
* A geo point value representing a point on the surface of Earth.
|
|
65
|
+
*/
|
|
66
|
+
export class LiteralGeoPointValue {
|
|
67
|
+
constructor(options) {
|
|
68
|
+
this.geoPointValue = options.geoPointValue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FirestoreConfig } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Function to create a JWT (JSON Web Token)
|
|
4
|
+
* @param config Firestore configuration
|
|
5
|
+
* @returns JWT string
|
|
6
|
+
*/
|
|
7
|
+
export declare function createJWT(config: FirestoreConfig): Promise<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Function to get Firestore authentication token
|
|
10
|
+
* @param config Firestore configuration
|
|
11
|
+
* @returns Access token
|
|
12
|
+
*/
|
|
13
|
+
export declare function getFirestoreToken(config: FirestoreConfig): Promise<string>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as jose from "jose";
|
|
2
|
+
/**
|
|
3
|
+
* Function to create a JWT (JSON Web Token)
|
|
4
|
+
* @param config Firestore configuration
|
|
5
|
+
* @returns JWT string
|
|
6
|
+
*/
|
|
7
|
+
export async function createJWT(config) {
|
|
8
|
+
const now = Math.floor(Date.now() / 1000);
|
|
9
|
+
const payload = {
|
|
10
|
+
iss: config.clientEmail,
|
|
11
|
+
sub: config.clientEmail,
|
|
12
|
+
aud: "https://oauth2.googleapis.com/token",
|
|
13
|
+
iat: now,
|
|
14
|
+
exp: now + 3600, // Expires in 1 hour
|
|
15
|
+
scope: "https://www.googleapis.com/auth/datastore",
|
|
16
|
+
};
|
|
17
|
+
try {
|
|
18
|
+
// Import the private key
|
|
19
|
+
const privateKey = await jose.importPKCS8(config.privateKey, "RS256");
|
|
20
|
+
// Create JWT
|
|
21
|
+
const token = await new jose.SignJWT(payload)
|
|
22
|
+
.setProtectedHeader({
|
|
23
|
+
alg: "RS256",
|
|
24
|
+
typ: "JWT",
|
|
25
|
+
})
|
|
26
|
+
.sign(privateKey);
|
|
27
|
+
return token;
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
console.error("Error creating JWT:", error);
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Function to get Firestore authentication token
|
|
36
|
+
* @param config Firestore configuration
|
|
37
|
+
* @returns Access token
|
|
38
|
+
*/
|
|
39
|
+
export async function getFirestoreToken(config) {
|
|
40
|
+
// No authentication in emulator mode (returns a dummy token)
|
|
41
|
+
if (config.useEmulator) {
|
|
42
|
+
return "firebase-emulator-auth-token";
|
|
43
|
+
}
|
|
44
|
+
// Normal authentication process
|
|
45
|
+
const response = await fetch("https://oauth2.googleapis.com/token", {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: {
|
|
48
|
+
"Content-Type": "application/json",
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify({
|
|
51
|
+
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
52
|
+
assertion: await createJWT(config),
|
|
53
|
+
}),
|
|
54
|
+
});
|
|
55
|
+
const data = (await response.json());
|
|
56
|
+
return data.access_token;
|
|
57
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 秘密鍵の文字列内にある改行コードのエスケープシーケンスを実際の改行に変換する
|
|
3
|
+
* @param privateKey 変換する秘密鍵文字列
|
|
4
|
+
* @returns 変換後の秘密鍵文字列
|
|
5
|
+
*/
|
|
6
|
+
export function formatPrivateKey(privateKey) {
|
|
7
|
+
if (privateKey.includes("\\n")) {
|
|
8
|
+
return privateKey.replace(/\\n/g, "\n");
|
|
9
|
+
}
|
|
10
|
+
return privateKey;
|
|
11
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { CommitWrite, FieldTransform, FirestoreDocument, FirestoreFieldValue, FirestoreResponse } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* JSの値をFirestore形式に変換する
|
|
4
|
+
* @param value 変換する値
|
|
5
|
+
* @returns Firestore形式の値
|
|
6
|
+
*/
|
|
7
|
+
export declare function convertToFirestoreValue(value: any): FirestoreFieldValue;
|
|
8
|
+
/**
|
|
9
|
+
* Firestore形式からJSの値に変換する
|
|
10
|
+
* @param firestoreValue Firestore形式の値
|
|
11
|
+
* @returns JS形式の値
|
|
12
|
+
*/
|
|
13
|
+
export declare function convertFromFirestoreValue(firestoreValue: FirestoreFieldValue): any;
|
|
14
|
+
/**
|
|
15
|
+
* オブジェクトをFirestoreドキュメント形式に変換
|
|
16
|
+
* @param data 変換するオブジェクト
|
|
17
|
+
* @returns Firestoreドキュメント
|
|
18
|
+
*/
|
|
19
|
+
export declare function convertToFirestoreDocument(data: Record<string, any>): FirestoreDocument;
|
|
20
|
+
/**
|
|
21
|
+
* Split write data into plain field values and Firestore field transforms.
|
|
22
|
+
*
|
|
23
|
+
* `FieldValue` sentinels (e.g. `serverTimestamp()`) are pulled out into
|
|
24
|
+
* transforms keyed by their (escaped, dot-separated) field path; everything
|
|
25
|
+
* else is left untouched in `fields`. Recursion only descends into plain
|
|
26
|
+
* objects, so class instances (Date / references / geo points) are treated as
|
|
27
|
+
* leaves.
|
|
28
|
+
*
|
|
29
|
+
* @param data Write data (JS values, may contain FieldValue sentinels)
|
|
30
|
+
* @param prefix Field-path prefix used while recursing (internal, pre-escaped)
|
|
31
|
+
*/
|
|
32
|
+
export declare function extractFieldTransforms(data: Record<string, any>, prefix?: string): {
|
|
33
|
+
fields: Record<string, any>;
|
|
34
|
+
transforms: FieldTransform[];
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Build a single `documents:commit` write that updates a document and applies
|
|
38
|
+
* field transforms. `updateTransforms` / `currentDocument` are only included
|
|
39
|
+
* when relevant.
|
|
40
|
+
*
|
|
41
|
+
* @param documentName Full resource name (projects/.../documents/<path>)
|
|
42
|
+
* @param fields Already-converted Firestore field values
|
|
43
|
+
* @param transforms Field transforms to apply after the update
|
|
44
|
+
* @param currentDocument Optional precondition (e.g. `{ exists: false }`)
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildCommitWrite(documentName: string, fields: Record<string, FirestoreFieldValue>, transforms: FieldTransform[], currentDocument?: {
|
|
47
|
+
exists?: boolean;
|
|
48
|
+
updateTime?: string;
|
|
49
|
+
}): CommitWrite;
|
|
50
|
+
/**
|
|
51
|
+
* Firestoreドキュメントをオブジェクトに変換
|
|
52
|
+
* @param doc Firestoreレスポンス
|
|
53
|
+
* @returns 変換されたオブジェクト(idプロパティ付き)
|
|
54
|
+
*/
|
|
55
|
+
export declare function convertFromFirestoreDocument(doc: FirestoreResponse): Record<string, any> & {
|
|
56
|
+
id: string;
|
|
57
|
+
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { DocumentReference } from "../client";
|
|
2
|
+
import { FieldValue } from "../field-value";
|
|
3
|
+
import { LiteralDocumentReference, LiteralGeoPointValue, } from "../types";
|
|
4
|
+
import { getDocumentId } from "./path";
|
|
5
|
+
/**
|
|
6
|
+
* JSの値をFirestore形式に変換する
|
|
7
|
+
* @param value 変換する値
|
|
8
|
+
* @returns Firestore形式の値
|
|
9
|
+
*/
|
|
10
|
+
export function convertToFirestoreValue(value) {
|
|
11
|
+
if (value instanceof FieldValue) {
|
|
12
|
+
// Sentinels (e.g. serverTimestamp) must be extracted into field transforms
|
|
13
|
+
// before conversion. Reaching here means one was used where Firestore
|
|
14
|
+
// cannot express a transform (such as inside an array).
|
|
15
|
+
throw new Error("FieldValue (e.g. serverTimestamp()) can only be used as a top-level or nested document field value, not inside an array.");
|
|
16
|
+
}
|
|
17
|
+
if (value instanceof Date) {
|
|
18
|
+
return { timestampValue: value.toISOString() };
|
|
19
|
+
}
|
|
20
|
+
else if (value instanceof DocumentReference) {
|
|
21
|
+
return { referenceValue: value.referenceValue };
|
|
22
|
+
}
|
|
23
|
+
else if (value instanceof LiteralDocumentReference) {
|
|
24
|
+
return { referenceValue: value.referenceValue };
|
|
25
|
+
}
|
|
26
|
+
else if (value instanceof LiteralGeoPointValue) {
|
|
27
|
+
return { geoPointValue: value.geoPointValue };
|
|
28
|
+
}
|
|
29
|
+
else if (typeof value === "string") {
|
|
30
|
+
return { stringValue: value };
|
|
31
|
+
}
|
|
32
|
+
else if (typeof value === "number") {
|
|
33
|
+
return Number.isInteger(value)
|
|
34
|
+
? { integerValue: value }
|
|
35
|
+
: { doubleValue: value };
|
|
36
|
+
}
|
|
37
|
+
else if (typeof value === "boolean") {
|
|
38
|
+
return { booleanValue: value };
|
|
39
|
+
}
|
|
40
|
+
else if (value === null || value === undefined) {
|
|
41
|
+
return { nullValue: null };
|
|
42
|
+
}
|
|
43
|
+
else if (Array.isArray(value)) {
|
|
44
|
+
return {
|
|
45
|
+
arrayValue: {
|
|
46
|
+
values: value.map(item => convertToFirestoreValue(item)),
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
else if (typeof value === "object") {
|
|
51
|
+
const fields = Object.entries(value).reduce((acc, [key, val]) => ({
|
|
52
|
+
...acc,
|
|
53
|
+
[key]: convertToFirestoreValue(val),
|
|
54
|
+
}), {});
|
|
55
|
+
return { mapValue: { fields } };
|
|
56
|
+
}
|
|
57
|
+
// デフォルトは文字列化
|
|
58
|
+
return { stringValue: String(value) };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Firestore形式からJSの値に変換する
|
|
62
|
+
* @param firestoreValue Firestore形式の値
|
|
63
|
+
* @returns JS形式の値
|
|
64
|
+
*/
|
|
65
|
+
export function convertFromFirestoreValue(firestoreValue) {
|
|
66
|
+
if ("stringValue" in firestoreValue) {
|
|
67
|
+
return firestoreValue.stringValue;
|
|
68
|
+
}
|
|
69
|
+
else if ("integerValue" in firestoreValue) {
|
|
70
|
+
return Number(firestoreValue.integerValue);
|
|
71
|
+
}
|
|
72
|
+
else if ("doubleValue" in firestoreValue) {
|
|
73
|
+
return firestoreValue.doubleValue;
|
|
74
|
+
}
|
|
75
|
+
else if ("booleanValue" in firestoreValue) {
|
|
76
|
+
return firestoreValue.booleanValue;
|
|
77
|
+
}
|
|
78
|
+
else if ("nullValue" in firestoreValue) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
else if ("timestampValue" in firestoreValue) {
|
|
82
|
+
return new Date(firestoreValue.timestampValue);
|
|
83
|
+
}
|
|
84
|
+
else if ("geoPointValue" in firestoreValue) {
|
|
85
|
+
return new LiteralGeoPointValue(firestoreValue);
|
|
86
|
+
}
|
|
87
|
+
else if ("referenceValue" in firestoreValue) {
|
|
88
|
+
return new LiteralDocumentReference(firestoreValue);
|
|
89
|
+
}
|
|
90
|
+
else if ("mapValue" in firestoreValue && firestoreValue.mapValue.fields) {
|
|
91
|
+
return Object.entries(firestoreValue.mapValue.fields).reduce((acc, [key, val]) => ({
|
|
92
|
+
...acc,
|
|
93
|
+
[key]: convertFromFirestoreValue(val),
|
|
94
|
+
}), {});
|
|
95
|
+
}
|
|
96
|
+
else if ("arrayValue" in firestoreValue) {
|
|
97
|
+
// The `values` field can be undefined, meaning that this is an empty array
|
|
98
|
+
return (firestoreValue.arrayValue.values ?? []).map(convertFromFirestoreValue);
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* オブジェクトをFirestoreドキュメント形式に変換
|
|
104
|
+
* @param data 変換するオブジェクト
|
|
105
|
+
* @returns Firestoreドキュメント
|
|
106
|
+
*/
|
|
107
|
+
export function convertToFirestoreDocument(data) {
|
|
108
|
+
return {
|
|
109
|
+
fields: Object.entries(data).reduce((acc, [key, value]) => ({
|
|
110
|
+
...acc,
|
|
111
|
+
[key]: convertToFirestoreValue(value),
|
|
112
|
+
}), {}),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Whether a value is a plain JS object (`{}` / `Object.create(null)`), as
|
|
117
|
+
* opposed to a class instance such as `Date`, `DocumentReference`,
|
|
118
|
+
* `LiteralGeoPointValue`, `LiteralDocumentReference`, `FieldValue`, or an array.
|
|
119
|
+
* Only plain objects are recursed into when extracting field transforms.
|
|
120
|
+
*/
|
|
121
|
+
function isPlainObject(value) {
|
|
122
|
+
if (value === null || typeof value !== "object")
|
|
123
|
+
return false;
|
|
124
|
+
const proto = Object.getPrototypeOf(value);
|
|
125
|
+
return proto === Object.prototype || proto === null;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Escape a single field name for use in a Firestore field path. Simple names
|
|
129
|
+
* (`[A-Za-z_][A-Za-z0-9_]*`) are used as-is; anything else (dashes, dots,
|
|
130
|
+
* leading digits, spaces, ...) is wrapped in backticks with `\` and `` ` ``
|
|
131
|
+
* escaped, so a literal dot in a key is treated as part of the name rather than
|
|
132
|
+
* a path separator.
|
|
133
|
+
* See: https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents#Document
|
|
134
|
+
*/
|
|
135
|
+
function escapeFieldPathSegment(segment) {
|
|
136
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(segment)) {
|
|
137
|
+
return segment;
|
|
138
|
+
}
|
|
139
|
+
return "`" + segment.replace(/\\/g, "\\\\").replace(/`/g, "\\`") + "`";
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Split write data into plain field values and Firestore field transforms.
|
|
143
|
+
*
|
|
144
|
+
* `FieldValue` sentinels (e.g. `serverTimestamp()`) are pulled out into
|
|
145
|
+
* transforms keyed by their (escaped, dot-separated) field path; everything
|
|
146
|
+
* else is left untouched in `fields`. Recursion only descends into plain
|
|
147
|
+
* objects, so class instances (Date / references / geo points) are treated as
|
|
148
|
+
* leaves.
|
|
149
|
+
*
|
|
150
|
+
* @param data Write data (JS values, may contain FieldValue sentinels)
|
|
151
|
+
* @param prefix Field-path prefix used while recursing (internal, pre-escaped)
|
|
152
|
+
*/
|
|
153
|
+
export function extractFieldTransforms(data, prefix = "") {
|
|
154
|
+
const fields = {};
|
|
155
|
+
const transforms = [];
|
|
156
|
+
for (const [key, value] of Object.entries(data)) {
|
|
157
|
+
const escapedKey = escapeFieldPathSegment(key);
|
|
158
|
+
const fieldPath = prefix ? `${prefix}.${escapedKey}` : escapedKey;
|
|
159
|
+
if (value instanceof FieldValue) {
|
|
160
|
+
if (value.methodName === "serverTimestamp") {
|
|
161
|
+
transforms.push({ fieldPath, setToServerValue: "REQUEST_TIME" });
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
throw new Error(`Unsupported FieldValue: ${value.methodName}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else if (isPlainObject(value)) {
|
|
168
|
+
const nested = extractFieldTransforms(value, fieldPath);
|
|
169
|
+
fields[key] = nested.fields;
|
|
170
|
+
transforms.push(...nested.transforms);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
fields[key] = value;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return { fields, transforms };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Build a single `documents:commit` write that updates a document and applies
|
|
180
|
+
* field transforms. `updateTransforms` / `currentDocument` are only included
|
|
181
|
+
* when relevant.
|
|
182
|
+
*
|
|
183
|
+
* @param documentName Full resource name (projects/.../documents/<path>)
|
|
184
|
+
* @param fields Already-converted Firestore field values
|
|
185
|
+
* @param transforms Field transforms to apply after the update
|
|
186
|
+
* @param currentDocument Optional precondition (e.g. `{ exists: false }`)
|
|
187
|
+
*/
|
|
188
|
+
export function buildCommitWrite(documentName, fields, transforms, currentDocument) {
|
|
189
|
+
const write = {
|
|
190
|
+
update: { name: documentName, fields },
|
|
191
|
+
};
|
|
192
|
+
if (transforms.length > 0) {
|
|
193
|
+
write.updateTransforms = transforms;
|
|
194
|
+
}
|
|
195
|
+
if (currentDocument) {
|
|
196
|
+
write.currentDocument = currentDocument;
|
|
197
|
+
}
|
|
198
|
+
return write;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Firestoreドキュメントをオブジェクトに変換
|
|
202
|
+
* @param doc Firestoreレスポンス
|
|
203
|
+
* @returns 変換されたオブジェクト(idプロパティ付き)
|
|
204
|
+
*/
|
|
205
|
+
export function convertFromFirestoreDocument(doc) {
|
|
206
|
+
if (!doc.fields)
|
|
207
|
+
return { id: getDocumentId(doc.name) };
|
|
208
|
+
const result = Object.entries(doc.fields).reduce((acc, [key, value]) => ({
|
|
209
|
+
...acc,
|
|
210
|
+
[key]: convertFromFirestoreValue(value),
|
|
211
|
+
}), {});
|
|
212
|
+
return {
|
|
213
|
+
...result,
|
|
214
|
+
id: getDocumentId(doc.name),
|
|
215
|
+
};
|
|
216
|
+
}
|