firebase-dataconnect-bootstrap 1.1.0 → 1.2.1

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 CHANGED
@@ -1,31 +1,49 @@
1
1
  # firebase-dataconnect-bootstrap
2
2
 
3
- Firebase Data Connect の基本構成作成と、Firestore の `onDocumentWritten` Cloud Functions 追加を自動化する CLI です。
3
+ Firebase Data Connect Cloud Functions のセットアップを自動化する CLI です。
4
+ 初期作成だけでなく、再実行で設定変更(途中更新)もできます。
4
5
 
5
6
  ## できること
6
7
 
7
- - 対象レポジトリへ `.firebaserc` と `firebase.json` を生成/更新
8
- - `dataconnect/` 配下に初期ファイル群を作成
9
- - `functions/` が未初期化なら最小構成で初期化
10
- - Firestore `onDocumentWritten` 関数を追加して export
11
- - 必要に応じて `functions/` で `npm install` を実行
8
+ - `.firebaserc` と `firebase.json` の生成/更新
9
+ - `dataconnect/` 初期ファイル生成
10
+ - `functions/` 未初期化時の最小構成作成
11
+ - Firestore `onDocumentWritten` の生成/更新
12
+ - ベクトル検索用 `onRequest` 関数の生成/更新
13
+ - 埋め込み実装差し替え用 `vectorSearchEmbedding` テンプレート生成
14
+ - 前回設定を `.firebase-dataconnect-bootstrap.json` に保存し、次回実行時に再利用
12
15
 
13
16
  ## 使い方
14
17
 
15
- 初回セットアップ:
16
-
17
18
  ```bash
18
- npm install
19
- npm run build
19
+ npx firebase-dataconnect-bootstrap
20
20
  ```
21
21
 
22
- 対話モード:
22
+ 対話モードは日本語で質問が表示されます。
23
+ 同じリポジトリで再実行すると、保存済み設定を初期値として設定変更できます。
24
+ 既存設定がある場合は、対話中に「新しいコレクション設定を追加するか」を選べます。
25
+
26
+ ## 追加コレクションを非対話で追加する例
23
27
 
24
28
  ```bash
25
- npx firebase-dataconnect-bootstrap
29
+ npx firebase-dataconnect-bootstrap \
30
+ --yes \
31
+ --add-collection \
32
+ --target . \
33
+ --project your-firebase-project-id \
34
+ --document 'articles/{articleId}' \
35
+ --function onArticlesWritten \
36
+ --vector-search \
37
+ --vector-collection 'articles' \
38
+ --source-text-field 'body' \
39
+ --vector-field 'embedding' \
40
+ --search-fields 'title,body,updatedAt' \
41
+ --search-function 'searchArticlesByVector' \
42
+ --top-k 8 \
43
+ --no-install
26
44
  ```
27
45
 
28
- 非対話モード:
46
+ ## 非対話モード例
29
47
 
30
48
  ```bash
31
49
  npx firebase-dataconnect-bootstrap \
@@ -37,30 +55,51 @@ npx firebase-dataconnect-bootstrap \
37
55
  --location asia-northeast1 \
38
56
  --document 'meetingSummaries/{summaryId}' \
39
57
  --function onMeetingSummaryWritten \
58
+ --vector-search \
59
+ --vector-collection 'meetingSummaries' \
60
+ --source-text-field 'summary' \
61
+ --vector-field 'embedding' \
62
+ --search-fields 'title,summary,createdAt' \
63
+ --search-function 'searchByVector' \
64
+ --top-k 5 \
40
65
  --install
41
66
  ```
42
67
 
43
- ## オプション
68
+ ## 主なオプション
69
+
70
+ - `--config <name>`: 設定保存ファイル名(既定: `.firebase-dataconnect-bootstrap.json`)
71
+ - `--add-collection`: 再実行時に既存設定へ新しいコレクション設定を追加
72
+ - `--vector-search` / `--no-vector-search`: ベクトル検索 scaffold の有効/無効
73
+ - `--vector-collection <path>`: 検索対象コレクション
74
+ - `--source-text-field <name>`: 埋め込み更新対象のテキストフィールド
75
+ - `--vector-field <name>`: ベクトル保存フィールド
76
+ - `--search-fields <csv>`: 検索結果として返すフィールド
77
+ - `--search-function <name>`: 生成する `onRequest` 関数名
78
+ - `--top-k <number>`: 検索時のデフォルト上位件数
44
79
 
45
- - `--target <path>`: 対象リポジトリ
46
- - `--project <id>`: Firebase project id
47
- - `--region <region>`: Functions region
48
- - `--service <serviceId>`: Data Connect service id
49
- - `--location <location>`: Data Connect location
50
- - `--document <path>`: Firestore document path
51
- - `--function <name>`: 関数の export 名
52
- - `--install`: `functions` で `npm install` を実行
53
- - `--no-install`: `npm install` をスキップ
54
- - `--yes`: 対話なしで実行(`--project` は必須)
80
+ ## 生成される主な関数(コレクションごと)
55
81
 
56
- ## npm 公開
82
+ - `onDocumentWritten_<functionName>.*`
83
+ - `sourceTextField` の内容を `embedText` に渡し、`vectorField` へ保存
84
+ - `vectorSearchOnRequest_<searchFunctionName>.*`
85
+ - HTTP リクエストの `vector`(または `query`)でコサイン類似度検索
86
+ - `vectorSearchEmbedding_<functionName>.*`
87
+ - `embedText` を実装する差し替えポイント(デフォルトは `null` を返す)
57
88
 
58
- 1. `npm login`
59
- 2. パッケージ名が未使用か確認
60
- 3. `npm run build`
61
- 4. `npm publish --access public`
89
+ ## npm モジュールとして検索クライアントを使う
62
90
 
63
- ## 注意点
91
+ ```ts
92
+ import { createVectorSearchClient } from "firebase-dataconnect-bootstrap/search-client";
93
+
94
+ const client = createVectorSearchClient({
95
+ endpoint: "https://<region>-<project>.cloudfunctions.net/searchByVector"
96
+ });
97
+
98
+ const result = await client.search({
99
+ query: "議事録の要点",
100
+ topK: 5
101
+ });
102
+ ```
64
103
 
65
- - Data Connect/Firebase CLI の認証やプロジェクト作成そのものは行いません。
66
- - 既存の `functions` 実装に追記するため、デプロイ前に差分確認を推奨します。
104
+ `query` を使う場合は、Functions `vectorSearchEmbedding.*` の `embedText` 実装が必要です。
105
+ `vector` を直接渡す場合は `embedText` が未実装でも検索できます。
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function runCli(argv?: string[]): Promise<void>;
package/dist/cli.js CHANGED
@@ -14,8 +14,18 @@ Options:
14
14
  --location <location> Data Connect location (default: same as region)
15
15
  --document <path> Firestore document path (default: meetingSummaries/{summaryId})
16
16
  --function <name> Function export name (default: onMeetingSummaryWritten)
17
+ --config <name> Saved config filename (default: .firebase-dataconnect-bootstrap.json)
18
+ --add-collection Add a new collection setting on re-run
17
19
  --install Run npm install in functions directory
18
20
  --no-install Skip npm install in functions directory
21
+ --vector-search Enable vector search scaffolding
22
+ --no-vector-search Disable vector search scaffolding
23
+ --vector-collection <path> Collection path used for vector search
24
+ --source-text-field <name> Source text field for embedding updates
25
+ --vector-field <name> Vector field name
26
+ --search-fields <csv> Response fields for search results (comma-separated)
27
+ --search-function <name> onRequest function export name for vector search
28
+ --top-k <number> Default topK value for vector search
19
29
  --yes Use defaults and skip prompts where possible
20
30
  -h, --help Show help
21
31
  `;
@@ -29,6 +39,9 @@ function parseArgs(argv) {
29
39
  documentPath: undefined,
30
40
  functionName: undefined,
31
41
  installDependencies: undefined,
42
+ configFileName: undefined,
43
+ vectorSearch: {},
44
+ addCollection: undefined,
32
45
  yes: false,
33
46
  help: false
34
47
  };
@@ -50,6 +63,18 @@ function parseArgs(argv) {
50
63
  args.installDependencies = false;
51
64
  continue;
52
65
  }
66
+ if (token === "--vector-search") {
67
+ args.vectorSearch = { ...(args.vectorSearch ?? {}), enabled: true };
68
+ continue;
69
+ }
70
+ if (token === "--no-vector-search") {
71
+ args.vectorSearch = { ...(args.vectorSearch ?? {}), enabled: false };
72
+ continue;
73
+ }
74
+ if (token === "--add-collection") {
75
+ args.addCollection = true;
76
+ continue;
77
+ }
53
78
  if (token.startsWith("--target=")) {
54
79
  args.targetDir = token.slice("--target=".length);
55
80
  continue;
@@ -78,12 +103,66 @@ function parseArgs(argv) {
78
103
  args.functionName = token.slice("--function=".length);
79
104
  continue;
80
105
  }
106
+ if (token.startsWith("--config=")) {
107
+ args.configFileName = token.slice("--config=".length);
108
+ continue;
109
+ }
110
+ if (token.startsWith("--vector-collection=")) {
111
+ args.vectorSearch = {
112
+ ...(args.vectorSearch ?? {}),
113
+ collectionPath: token.slice("--vector-collection=".length)
114
+ };
115
+ continue;
116
+ }
117
+ if (token.startsWith("--source-text-field=")) {
118
+ args.vectorSearch = {
119
+ ...(args.vectorSearch ?? {}),
120
+ sourceTextField: token.slice("--source-text-field=".length)
121
+ };
122
+ continue;
123
+ }
124
+ if (token.startsWith("--vector-field=")) {
125
+ args.vectorSearch = {
126
+ ...(args.vectorSearch ?? {}),
127
+ vectorField: token.slice("--vector-field=".length)
128
+ };
129
+ continue;
130
+ }
131
+ if (token.startsWith("--search-fields=")) {
132
+ args.vectorSearch = {
133
+ ...(args.vectorSearch ?? {}),
134
+ returnFields: token
135
+ .slice("--search-fields=".length)
136
+ .split(",")
137
+ .map((field) => field.trim())
138
+ .filter(Boolean)
139
+ };
140
+ continue;
141
+ }
142
+ if (token.startsWith("--search-function=")) {
143
+ args.vectorSearch = {
144
+ ...(args.vectorSearch ?? {}),
145
+ functionName: token.slice("--search-function=".length)
146
+ };
147
+ continue;
148
+ }
149
+ if (token.startsWith("--top-k=")) {
150
+ const parsedTopK = Number.parseInt(token.slice("--top-k=".length), 10);
151
+ if (Number.isNaN(parsedTopK) || parsedTopK <= 0) {
152
+ throw new Error("--top-k must be a positive integer");
153
+ }
154
+ args.vectorSearch = { ...(args.vectorSearch ?? {}), defaultTopK: parsedTopK };
155
+ continue;
156
+ }
81
157
  const nextValue = argv[i + 1];
82
- const useNext = (field) => {
158
+ const requireNextValue = () => {
83
159
  if (!nextValue || nextValue.startsWith("-")) {
84
160
  throw new Error(`Missing value for ${token}`);
85
161
  }
86
- args[field] = nextValue;
162
+ return nextValue;
163
+ };
164
+ const useNext = (field) => {
165
+ args[field] = requireNextValue();
87
166
  i += 1;
88
167
  };
89
168
  switch (token) {
@@ -108,6 +187,56 @@ function parseArgs(argv) {
108
187
  case "--function":
109
188
  useNext("functionName");
110
189
  break;
190
+ case "--config":
191
+ useNext("configFileName");
192
+ break;
193
+ case "--vector-collection":
194
+ args.vectorSearch = {
195
+ ...(args.vectorSearch ?? {}),
196
+ collectionPath: requireNextValue()
197
+ };
198
+ i += 1;
199
+ break;
200
+ case "--source-text-field":
201
+ args.vectorSearch = {
202
+ ...(args.vectorSearch ?? {}),
203
+ sourceTextField: requireNextValue()
204
+ };
205
+ i += 1;
206
+ break;
207
+ case "--vector-field":
208
+ args.vectorSearch = {
209
+ ...(args.vectorSearch ?? {}),
210
+ vectorField: requireNextValue()
211
+ };
212
+ i += 1;
213
+ break;
214
+ case "--search-fields":
215
+ args.vectorSearch = {
216
+ ...(args.vectorSearch ?? {}),
217
+ returnFields: requireNextValue()
218
+ .split(",")
219
+ .map((field) => field.trim())
220
+ .filter(Boolean)
221
+ };
222
+ i += 1;
223
+ break;
224
+ case "--search-function":
225
+ args.vectorSearch = {
226
+ ...(args.vectorSearch ?? {}),
227
+ functionName: requireNextValue()
228
+ };
229
+ i += 1;
230
+ break;
231
+ case "--top-k": {
232
+ const parsedTopK = Number.parseInt(requireNextValue(), 10);
233
+ if (Number.isNaN(parsedTopK) || parsedTopK <= 0) {
234
+ throw new Error("--top-k must be a positive integer");
235
+ }
236
+ args.vectorSearch = { ...(args.vectorSearch ?? {}), defaultTopK: parsedTopK };
237
+ i += 1;
238
+ break;
239
+ }
111
240
  default:
112
241
  throw new Error(`Unknown option: ${token}`);
113
242
  }
@@ -0,0 +1,6 @@
1
+ export { run } from "./main.js";
2
+ export { runCli } from "./cli.js";
3
+ export { collectConfig } from "./prompt.js";
4
+ export { createVectorSearchClient, searchByVector } from "./search-client.js";
5
+ export type { CliArgs, PersistedBootstrapConfig, RunResult, SetupConfig, SetupTargetConfig, VectorSearchConfig } from "./types.js";
6
+ export type { SearchByVectorInput, VectorSearchClientOptions, VectorSearchHit, VectorSearchResponse } from "./search-client.js";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { run } from "./main.js";
2
+ export { runCli } from "./cli.js";
3
+ export { collectConfig } from "./prompt.js";
4
+ export { createVectorSearchClient, searchByVector } from "./search-client.js";
package/dist/main.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { RunResult, SetupConfig } from "./types.js";
2
+ export declare function run(config: SetupConfig): Promise<RunResult>;