firebase-tools 14.15.0 → 14.15.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/lib/command.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validateProjectId = exports.Command = void 0;
4
4
  const clc = require("colorette");
5
+ const path = require("node:path");
5
6
  const lodash_1 = require("lodash");
6
7
  const error_1 = require("./error");
7
8
  const utils_1 = require("./utils");
@@ -193,17 +194,15 @@ class Command {
193
194
  }
194
195
  }
195
196
  async applyRC(options) {
196
- var _a, _b;
197
+ var _a;
197
198
  const rc = (0, rc_1.loadRC)(options);
198
199
  options.rc = rc;
199
- let activeProject = options.projectRoot
200
- ? ((_a = configstore_1.configstore.get("activeProjects")) !== null && _a !== void 0 ? _a : {})[options.projectRoot]
201
- : undefined;
200
+ let activeProject = this.configstoreProject(options.projectRoot || process.cwd());
202
201
  const isUseCommand = process.argv.includes("use");
203
202
  if ((0, env_1.isFirebaseStudio)() && !options.project && !isUseCommand) {
204
203
  activeProject = await (0, studio_1.reconcileStudioFirebaseProject)(options, activeProject);
205
204
  }
206
- options.project = (_b = options.project) !== null && _b !== void 0 ? _b : activeProject;
205
+ options.project = (_a = options.project) !== null && _a !== void 0 ? _a : activeProject;
207
206
  if (options.config && !options.project) {
208
207
  options.project = options.config.defaults.project;
209
208
  }
@@ -222,6 +221,21 @@ class Command {
222
221
  options.project = aliases["default"];
223
222
  }
224
223
  }
224
+ configstoreProject(dir) {
225
+ var _a;
226
+ const projectMap = (_a = configstore_1.configstore.get("activeProjects")) !== null && _a !== void 0 ? _a : {};
227
+ let currentDir = path.resolve(dir);
228
+ while (true) {
229
+ if (projectMap[currentDir]) {
230
+ return projectMap[currentDir];
231
+ }
232
+ const parentDir = path.dirname(currentDir);
233
+ if (parentDir === currentDir) {
234
+ return null;
235
+ }
236
+ currentDir = parentDir;
237
+ }
238
+ }
225
239
  async resolveProjectIdentifiers(options) {
226
240
  var _a;
227
241
  if ((_a = options.project) === null || _a === void 0 ? void 0 : _a.match(/^\d+$/)) {
@@ -16,6 +16,7 @@ const utils = require("../utils");
16
16
  const experiments_1 = require("../experiments");
17
17
  const templates_1 = require("../templates");
18
18
  const error_1 = require("../error");
19
+ const utils_1 = require("../utils");
19
20
  const homeDir = os.homedir();
20
21
  const BANNER_TEXT = (0, templates_1.readTemplateSync)("banner.txt");
21
22
  const GITIGNORE_TEMPLATE = (0, templates_1.readTemplateSync)("_gitignore");
@@ -166,6 +167,7 @@ async function initAction(feature, options) {
166
167
  json: true,
167
168
  fallback: {},
168
169
  }),
170
+ instructions: [],
169
171
  };
170
172
  if (process.platform === "win32") {
171
173
  if (!(await (0, prompt_1.confirm)("Are you ready to proceed?"))) {
@@ -218,5 +220,11 @@ async function initAction(feature, options) {
218
220
  }
219
221
  logger_1.logger.info();
220
222
  utils.logSuccess("Firebase initialization complete!");
223
+ if (setup.instructions.length) {
224
+ logger_1.logger.info(`\n${clc.bold("To get started:")}\n`);
225
+ for (const i of setup.instructions) {
226
+ (0, utils_1.logBullet)(i + "\n");
227
+ }
228
+ }
221
229
  }
222
230
  exports.initAction = initAction;
@@ -116,6 +116,11 @@ exports.ALL_EXPERIMENTS = experiments({
116
116
  default: true,
117
117
  public: false,
118
118
  },
119
+ mcpalpha: {
120
+ shortDescription: "Opt-in to early MCP features before they're widely released.",
121
+ default: false,
122
+ public: true,
123
+ },
119
124
  apptesting: {
120
125
  shortDescription: "Adds experimental App Testing feature",
121
126
  public: true,
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.newUniqueId = exports.toDNSCompatibleId = exports.postSetup = exports.actuate = exports.askQuestions = void 0;
3
+ exports.newUniqueId = exports.toDNSCompatibleId = exports.actuate = exports.askQuestions = void 0;
4
4
  const path_1 = require("path");
5
5
  const clc = require("colorette");
6
6
  const fs = require("fs-extra");
@@ -103,6 +103,15 @@ async function actuate(setup, config, options) {
103
103
  flow: info.analyticsFlow,
104
104
  });
105
105
  }
106
+ if (info.appDescription) {
107
+ setup.instructions.push(`You can visualize the Data Connect Schema in Firebase Console:
108
+
109
+ https://console.firebase.google.com/project/${setup.projectId}/dataconnect/locations/${info.locationId}/services/${info.serviceId}/schema`);
110
+ }
111
+ if (!setup.isBillingEnabled) {
112
+ setup.instructions.push((0, freeTrial_1.upgradeInstructions)(setup.projectId || "your-firebase-project"));
113
+ }
114
+ setup.instructions.push(`Install the Data Connect VS Code Extensions. You can explore Data Connect Query on local pgLite and Cloud SQL Postgres Instance.`);
106
115
  }
107
116
  exports.actuate = actuate;
108
117
  async function actuateWithInfo(setup, config, info, options) {
@@ -222,30 +231,6 @@ function schemasDeploySequence(projectId, info, schemaFiles, linkToCloudSql) {
222
231
  },
223
232
  ];
224
233
  }
225
- async function postSetup(setup) {
226
- var _a;
227
- const info = (_a = setup.featureInfo) === null || _a === void 0 ? void 0 : _a.dataconnect;
228
- if (!info) {
229
- throw new Error("Data Connect feature RequiredInfo is not provided");
230
- }
231
- const instructions = [];
232
- if (info.appDescription) {
233
- instructions.push(`You can visualize the Data Connect Schema in Firebase Console:
234
-
235
- https://console.firebase.google.com/project/${setup.projectId}/dataconnect/locations/${info.locationId}/services/${info.serviceId}/schema`);
236
- }
237
- if (!setup.isBillingEnabled) {
238
- instructions.push((0, freeTrial_1.upgradeInstructions)(setup.projectId || "your-firebase-project"));
239
- }
240
- instructions.push(`Install the Data Connect VS Code Extensions. You can explore Data Connect Query on local pgLite and Cloud SQL Postgres Instance.`);
241
- if (instructions.length) {
242
- logger_1.logger.info(`\n${clc.bold("To get started with Firebase Data Connect:")}`);
243
- for (const i of instructions) {
244
- (0, utils_1.logBullet)(i + "\n");
245
- }
246
- }
247
- }
248
- exports.postSetup = postSetup;
249
234
  async function writeFiles(config, info, serviceGql, options) {
250
235
  const dir = config.get("dataconnect.source") || "dataconnect";
251
236
  const subbedDataconnectYaml = subDataconnectYamlValues(Object.assign(Object.assign({}, info), { connectorDirs: serviceGql.connectors.map((c) => c.path) }));
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.aitools = exports.apptestingAcutate = exports.apptestingAskQuestions = exports.genkit = exports.apphosting = exports.dataconnectSdkActuate = exports.dataconnectSdkAskQuestions = exports.dataconnectPostSetup = exports.dataconnectActuate = exports.dataconnectAskQuestions = exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storageActuate = exports.storageAskQuestions = exports.hosting = exports.functions = exports.firestoreActuate = exports.firestoreAskQuestions = exports.databaseActuate = exports.databaseAskQuestions = exports.account = void 0;
3
+ exports.aitools = exports.apptestingAcutate = exports.apptestingAskQuestions = exports.genkit = exports.apphosting = exports.dataconnectSdkActuate = exports.dataconnectSdkAskQuestions = exports.dataconnectActuate = exports.dataconnectAskQuestions = exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storageActuate = exports.storageAskQuestions = exports.hosting = exports.functions = exports.firestoreActuate = exports.firestoreAskQuestions = exports.databaseActuate = exports.databaseAskQuestions = exports.account = void 0;
4
4
  var account_1 = require("./account");
5
5
  Object.defineProperty(exports, "account", { enumerable: true, get: function () { return account_1.doSetup; } });
6
6
  var database_1 = require("./database");
@@ -29,7 +29,6 @@ Object.defineProperty(exports, "hostingGithub", { enumerable: true, get: functio
29
29
  var dataconnect_1 = require("./dataconnect");
30
30
  Object.defineProperty(exports, "dataconnectAskQuestions", { enumerable: true, get: function () { return dataconnect_1.askQuestions; } });
31
31
  Object.defineProperty(exports, "dataconnectActuate", { enumerable: true, get: function () { return dataconnect_1.actuate; } });
32
- Object.defineProperty(exports, "dataconnectPostSetup", { enumerable: true, get: function () { return dataconnect_1.postSetup; } });
33
32
  var sdk_1 = require("./dataconnect/sdk");
34
33
  Object.defineProperty(exports, "dataconnectSdkAskQuestions", { enumerable: true, get: function () { return sdk_1.askQuestions; } });
35
34
  Object.defineProperty(exports, "dataconnectSdkActuate", { enumerable: true, get: function () { return sdk_1.actuate; } });
package/lib/init/index.js CHANGED
@@ -23,7 +23,6 @@ const featuresList = [
23
23
  name: "dataconnect",
24
24
  askQuestions: features.dataconnectAskQuestions,
25
25
  actuate: features.dataconnectActuate,
26
- postSetup: features.dataconnectPostSetup,
27
26
  },
28
27
  {
29
28
  name: "dataconnect:sdk",
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dataconnectPrompts = void 0;
4
+ const experiments_1 = require("../../../experiments");
5
+ const schema_1 = require("./schema");
6
+ exports.dataconnectPrompts = [];
7
+ if ((0, experiments_1.isEnabled)("mcpalpha")) {
8
+ exports.dataconnectPrompts.push(schema_1.schema);
9
+ }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.schema = void 0;
4
+ const prompt_1 = require("../../prompt");
5
+ const load_1 = require("../../../dataconnect/load");
6
+ const content_1 = require("../../util/dataconnect/content");
7
+ const compile_1 = require("../../util/dataconnect/compile");
8
+ function renderServices(fdcServices) {
9
+ var _a;
10
+ if (!fdcServices.length)
11
+ return "Data Connect Status: <UNCONFIGURED>";
12
+ return `\n\n## Data Connect Schema
13
+
14
+ The following is the up-to-date content of existing schema files (their paths are relative to the Data Connect source directory).
15
+
16
+ ${(_a = fdcServices[0].schema.source.files) === null || _a === void 0 ? void 0 : _a.map((f) => `\`\`\`graphql ${f.path}\n${f.content}\n\`\`\``).join("\n\n")}`;
17
+ }
18
+ function renderErrors(errors) {
19
+ return `\n\n## Current Schema Build Errors\n\n${errors || "<NO ERRORS>"}`;
20
+ }
21
+ exports.schema = (0, prompt_1.prompt)({
22
+ name: "schema",
23
+ description: "Generate or update your Firebase Data Connect schema.",
24
+ arguments: [
25
+ {
26
+ name: "prompt",
27
+ description: "describe the schema you want generated or the edits you want to make to your existing schema",
28
+ required: true,
29
+ },
30
+ ],
31
+ annotations: {
32
+ title: "Generate Data Connect Schema",
33
+ },
34
+ }, async ({ prompt }, { config, projectId, accountEmail }) => {
35
+ const fdcServices = await (0, load_1.loadAll)(projectId, config);
36
+ const buildErrors = fdcServices.length
37
+ ? await (0, compile_1.compileErrors)(fdcServices[0].sourceDirectory)
38
+ : "";
39
+ return [
40
+ {
41
+ role: "user",
42
+ content: {
43
+ type: "text",
44
+ text: `
45
+ ${content_1.MAIN_INSTRUCTIONS}\n\n${content_1.BUILTIN_SDL}
46
+
47
+ ==== CURRENT ENVIRONMENT INFO ====
48
+
49
+ User Email: ${accountEmail || "<NONE>"}
50
+ Project ID: ${projectId || "<NONE>"}
51
+ ${renderServices(fdcServices)}${renderErrors(buildErrors)}
52
+
53
+ ==== USER PROMPT ====
54
+
55
+ ${prompt}
56
+
57
+ ==== TASK INSTRUCTIONS ====
58
+
59
+ 1. If Data Connect is marked as \`<UNCONFIGURED>\`, first run the \`firebase_init\` tool with \`{dataconnect: {}}\` arguments to initialize it.
60
+ 2. If there is not an existing schema to work with (or the existing schema is the commented-out default schema about a movie app), follow the user's prompt to generate a robust schema meeting the specified requirements.
61
+ 3. If there is already a schema, perform edits to the existing schema file(s) based on the user's instructions. If schema build errors are present and seem relevant to your changes, attempt to fix them.
62
+ 4. After you have performed edits on the schema, run the \`dataconnect_compile\` tool to build the schema and see if there are any errors. Fix errors that are related to the user's prompt or your changes.
63
+ 5. If there are errors, attempt to fix them. If you have attempted to fix them 3 times without success, ask the user for help.
64
+ 6. If there are no errors, write a brief paragraph summarizing your changes.`,
65
+ },
66
+ },
67
+ ];
68
+ });
@@ -2,12 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.availablePrompts = void 0;
4
4
  const core_1 = require("./core");
5
+ const dataconnect_1 = require("./dataconnect");
5
6
  const crashlytics_1 = require("./crashlytics");
6
7
  const prompts = {
7
8
  core: core_1.corePrompts,
8
9
  firestore: [],
9
10
  storage: [],
10
- dataconnect: [],
11
+ dataconnect: dataconnect_1.dataconnectPrompts,
11
12
  auth: [],
12
13
  messaging: [],
13
14
  remoteconfig: [],
@@ -149,10 +149,19 @@ exports.init = (0, tool_1.tool)({
149
149
  projectId: projectId,
150
150
  features: [...featuresList],
151
151
  featureInfo: featureInfo,
152
+ instructions: [],
152
153
  };
153
154
  await (0, index_1.actuate)(setup, config, { force: true });
154
155
  config.writeProjectFile("firebase.json", setup.config);
155
156
  config.writeProjectFile(".firebaserc", setup.rcfile);
156
- return (0, util_1.toContent)(`Successfully setup the project ${projectId} with those features: ${featuresList.join(", ")}` +
157
- " To deploy them, you can run `firebase deploy` in command line.");
157
+ if (featureInfo.dataconnectSdk && !featureInfo.dataconnectSdk.apps.length) {
158
+ setup.instructions.push(`No app is found in the current folder. We recommend you create an app (web, ios, android) first, then re-run the 'firebase_init' MCP tool to add Data Connect SDKs to your apps.
159
+ Consider popular commands like 'npx create-react-app my-app', 'npx create-next-app my-app', 'flutter create my-app', etc`);
160
+ }
161
+ return (0, util_1.toContent)(`Successfully setup those features: ${featuresList.join(", ")}
162
+
163
+ To get started:
164
+
165
+ - ${setup.instructions.join("\n\n- ")}
166
+ `);
158
167
  });
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compile = void 0;
4
+ const zod_1 = require("zod");
5
+ const tool_1 = require("../../tool");
6
+ const load_1 = require("../../../dataconnect/load");
7
+ const compile_1 = require("../../util/dataconnect/compile");
8
+ exports.compile = (0, tool_1.tool)({
9
+ name: "build",
10
+ description: "Use this to compile Firebase Data Connect schema, operations, and/or connectors and check for build errors.",
11
+ inputSchema: zod_1.z.object({
12
+ error_filter: zod_1.z
13
+ .enum(["all", "schema", "operations"])
14
+ .describe("filter errors to a specific type only. defaults to `all` if omitted.")
15
+ .optional(),
16
+ service_id: zod_1.z
17
+ .string()
18
+ .optional()
19
+ .describe("The Firebase Data Connect service ID to look for. If omitted, builds all services defined in `firebase.json`."),
20
+ }),
21
+ annotations: {
22
+ title: "Compile Data Connect",
23
+ readOnlyHint: true,
24
+ },
25
+ _meta: {
26
+ requiresProject: false,
27
+ requiresAuth: false,
28
+ },
29
+ }, async ({ service_id, error_filter }, { projectId, config }) => {
30
+ const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
31
+ const errors = await (0, compile_1.compileErrors)(serviceInfo.sourceDirectory, error_filter);
32
+ if (errors)
33
+ return {
34
+ content: [
35
+ {
36
+ type: "text",
37
+ text: `The following errors were encountered while compiling Data Connect from directory \`${serviceInfo.sourceDirectory}\`:\n\n${errors}`,
38
+ },
39
+ ],
40
+ isError: true,
41
+ };
42
+ return { content: [{ type: "text", text: "Compiled successfully." }] };
43
+ });
@@ -5,8 +5,8 @@ const zod_1 = require("zod");
5
5
  const tool_1 = require("../../tool");
6
6
  const dataplane = require("../../../dataconnect/dataplaneClient");
7
7
  const load_1 = require("../../../dataconnect/load");
8
- const converter_1 = require("./converter");
9
- const emulator_1 = require("./emulator");
8
+ const converter_1 = require("../../util/dataconnect/converter");
9
+ const emulator_1 = require("../../util/dataconnect/emulator");
10
10
  exports.execute_graphql = (0, tool_1.tool)({
11
11
  name: "execute_graphql",
12
12
  description: "Executes an arbitrary GraphQL against a Data Connect service or its emulator.",
@@ -5,8 +5,8 @@ const zod_1 = require("zod");
5
5
  const tool_1 = require("../../tool");
6
6
  const dataplane = require("../../../dataconnect/dataplaneClient");
7
7
  const load_1 = require("../../../dataconnect/load");
8
- const converter_1 = require("./converter");
9
- const emulator_1 = require("./emulator");
8
+ const converter_1 = require("../../util/dataconnect/converter");
9
+ const emulator_1 = require("../../util/dataconnect/emulator");
10
10
  exports.execute_graphql_read = (0, tool_1.tool)({
11
11
  name: "execute_graphql_read",
12
12
  description: "Executes an arbitrary GraphQL query against a Data Connect service or its emulator. Cannot write data.",
@@ -6,8 +6,8 @@ const tool_1 = require("../../tool");
6
6
  const util_1 = require("../../util");
7
7
  const dataplane = require("../../../dataconnect/dataplaneClient");
8
8
  const load_1 = require("../../../dataconnect/load");
9
- const converter_1 = require("./converter");
10
- const emulator_1 = require("./emulator");
9
+ const converter_1 = require("../../util/dataconnect/converter");
10
+ const emulator_1 = require("../../util/dataconnect/emulator");
11
11
  exports.execute_mutation = (0, tool_1.tool)({
12
12
  name: "execute_mutation",
13
13
  description: "Executes a deployed Data Connect mutation against a service or its emulator. Can read and write data.",
@@ -6,8 +6,8 @@ const tool_1 = require("../../tool");
6
6
  const util_1 = require("../../util");
7
7
  const dataplane = require("../../../dataconnect/dataplaneClient");
8
8
  const load_1 = require("../../../dataconnect/load");
9
- const converter_1 = require("./converter");
10
- const emulator_1 = require("./emulator");
9
+ const converter_1 = require("../../util/dataconnect/converter");
10
+ const emulator_1 = require("../../util/dataconnect/emulator");
11
11
  exports.execute_query = (0, tool_1.tool)({
12
12
  name: "execute_query",
13
13
  description: "Executes a deployed Data Connect query against a service or its emulator. Cannot write any data.",
@@ -6,7 +6,7 @@ const tool_1 = require("../../tool");
6
6
  const util_1 = require("../../util");
7
7
  const client = require("../../../dataconnect/client");
8
8
  const load_1 = require("../../../dataconnect/load");
9
- const converter_1 = require("./converter");
9
+ const converter_1 = require("../../util/dataconnect/converter");
10
10
  exports.get_connectors = (0, tool_1.tool)({
11
11
  name: "get_connectors",
12
12
  description: "Get the Firebase Data Connect Connectors in the project, which includes the pre-defined GraphQL queries accessible to client SDKs.",
@@ -6,7 +6,7 @@ const tool_1 = require("../../tool");
6
6
  const util_1 = require("../../util");
7
7
  const client = require("../../../dataconnect/client");
8
8
  const load_1 = require("../../../dataconnect/load");
9
- const converter_1 = require("./converter");
9
+ const converter_1 = require("../../util/dataconnect/converter");
10
10
  exports.get_schema = (0, tool_1.tool)({
11
11
  name: "get_schema",
12
12
  description: "Retrieve information about the Firebase Data Connect Schema in the project, including Cloud SQL data sources and the GraphQL Schema describing the data model.",
@@ -10,7 +10,9 @@ const execute_graphql_1 = require("./execute_graphql");
10
10
  const execute_graphql_read_1 = require("./execute_graphql_read");
11
11
  const execute_query_1 = require("./execute_query");
12
12
  const execute_mutation_1 = require("./execute_mutation");
13
+ const compile_1 = require("./compile");
13
14
  exports.dataconnectTools = [
15
+ compile_1.compile,
14
16
  list_services_1.list_services,
15
17
  generate_schema_1.generate_schema,
16
18
  generate_operation_1.generate_operation,
@@ -5,7 +5,6 @@ const zod_1 = require("zod");
5
5
  const tool_1 = require("../../tool");
6
6
  const util_1 = require("../../util");
7
7
  const firestore_1 = require("../../../gcp/firestore");
8
- const errors_1 = require("../../errors");
9
8
  const types_1 = require("../../../emulator/types");
10
9
  exports.list_collections = (0, tool_1.tool)({
11
10
  name: "list_collections",
@@ -30,7 +29,5 @@ exports.list_collections = (0, tool_1.tool)({
30
29
  if (use_emulator) {
31
30
  emulatorUrl = await host.getEmulatorUrl(types_1.Emulators.FIRESTORE);
32
31
  }
33
- if (!projectId)
34
- return errors_1.NO_PROJECT_ERROR;
35
32
  return (0, util_1.toContent)(await (0, firestore_1.listCollectionIds)(projectId, database, emulatorUrl));
36
33
  });
@@ -16,6 +16,9 @@ function availableTools(activeFeatures) {
16
16
  if (!(activeFeatures === null || activeFeatures === void 0 ? void 0 : activeFeatures.length)) {
17
17
  activeFeatures = Object.keys(tools);
18
18
  }
19
+ if (!activeFeatures.includes("core")) {
20
+ activeFeatures.unshift("core");
21
+ }
19
22
  for (const key of activeFeatures) {
20
23
  toolDefs.push(...tools[key]);
21
24
  }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compileErrors = void 0;
4
+ const graphqlError_1 = require("../../../dataconnect/graphqlError");
5
+ const dataconnectEmulator_1 = require("../../../emulator/dataconnectEmulator");
6
+ async function compileErrors(configDir, errorFilter) {
7
+ const errors = (await dataconnectEmulator_1.DataConnectEmulator.build({ configDir })).errors;
8
+ return ((errors === null || errors === void 0 ? void 0 : errors.filter((e) => {
9
+ var _a;
10
+ const isOperationError = ["query", "mutation"].includes((_a = e.path) === null || _a === void 0 ? void 0 : _a[0]);
11
+ if (errorFilter === "operations")
12
+ return isOperationError;
13
+ if (errorFilter === "schema")
14
+ return !isOperationError;
15
+ return true;
16
+ }).map(graphqlError_1.prettify).join("\n")) || "");
17
+ }
18
+ exports.compileErrors = compileErrors;
@@ -0,0 +1,655 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BUILTIN_SDL = exports.MAIN_INSTRUCTIONS = void 0;
4
+ exports.MAIN_INSTRUCTIONS = `
5
+ Closely follow the following instructions:
6
+
7
+ You are Firebase Data Connect expert that is responsible for creating data connect schemas code in GraphQL for users.You will be given a description of the desired schema using Firebase Data Connect and your task is to write the schema code in GraphQL that fulfills the requirements and correct any mistakes in your generation.
8
+
9
+ For example, if I were to ask for a schema for a GraphQL database that contains a table called "users" with a field called "name" and another table called "posts" with a field called "body", I would get the following schema:
10
+ \`\`\`
11
+ type User @table {
12
+ name: String!
13
+ }
14
+
15
+ type Post @table {
16
+ body: String!
17
+ author: User
18
+ }
19
+ \`\`\`
20
+
21
+ Simple Firebase Data Connect schema often takes the following form:
22
+ \`\`\`graphql
23
+ type TableName @table {
24
+ uuidField: UUID
25
+ uuidArrayField: [UUID]
26
+ stringField: String
27
+ stringArrayField: [String]
28
+ intField: Int
29
+ intArrayField: [Int]
30
+ int64Field: Int64
31
+ int64ArrayField: [Int64]
32
+ floatField: Float
33
+ floatArrayField: [Float]
34
+ booleanField: Boolean
35
+ booleanArrayField: [Boolean]
36
+ timestampField: Timestamp
37
+ timestampArrayField: [Timestamp]
38
+ dateField: Date
39
+ dateArrayField: [Date]
40
+ vectorField: Vector @col(size:168)
41
+ }
42
+ \`\`\`
43
+
44
+ Leave out objects named after \`Query\` and \`Mutation\`
45
+
46
+ Firebase Data Connect implicitly adds \`id: UUID!\` to every table and implicitly makes it primary key. Therefore, leave out the \`id\` field.
47
+
48
+ Use \`UUID\` type instead of \`ID\` type or \`String\` type for id-like fields.
49
+
50
+ Array reference fields, like \`[SomeTable]\` and \`[SomeTable!]!\`, are not supported. Use the singular reference field instead.
51
+ For example, for a one-to-many relationship like one user is assiend to many bugs in a software project:
52
+ \`\`\`graphql
53
+ type User @table {
54
+ name: String!
55
+ # bugs: [Bug] # Not supported. Do not use
56
+ }
57
+
58
+ type Bug @table {
59
+ title: String!
60
+ assignee: User
61
+ reporter: User
62
+ }
63
+ \`\`\`
64
+
65
+ For another example, for a many-to-many relationship like each crew member is assigned to many chores and each chores requires many crews to complete:
66
+ \`\`\`graphql
67
+ type Crew @table {
68
+ name: String!
69
+ # assignedChores: [Chore!]! # No supported. Do not use
70
+ }
71
+
72
+ type Chore @table {
73
+ name: String!
74
+ description: String!
75
+ # assignedCrews: [Crews!]! # No supported. Do not use
76
+ }
77
+
78
+ type Assignment @table(key: ["crew", "chore"]) {
79
+ crew: Crew!
80
+ chore: Chore!
81
+ }
82
+ \`\`\`
83
+
84
+ Leave out \`@relation\` because it is not supported yet.
85
+
86
+ Leave out \`directive\`, \`enum\` and \`scalar\`.
87
+
88
+ Leave out \`@view\`.
89
+
90
+ Be sure that your response contains a valid Firebase Data Connect schema in a single GraphQL code block inside of triple backticks and closely follows my instructions and description.
91
+ `.trim();
92
+ exports.BUILTIN_SDL = `
93
+ # Directives
94
+
95
+ Directives define specific behaviors that can be applied to fields or types within a GraphQL schema.
96
+
97
+ ## Data Connect Defined
98
+
99
+ ### @col on \`FIELD_DEFINITION\` {:#col}
100
+ Customizes a field that represents a SQL database table column.
101
+
102
+ Data Connect maps scalar Fields on [\`@table\`](directive.md#table) type to a SQL column of
103
+ corresponding data type.
104
+
105
+ - scalar [\`UUID\`](scalar.md#UUID) maps to [\`uuid\`](https://www.postgresql.org/docs/current/datatype-uuid.html).
106
+ - scalar [\`String\`](scalar.md#String) maps to [\`text\`](https://www.postgresql.org/docs/current/datatype-character.html).
107
+ - scalar [\`Int\`](scalar.md#Int) maps to [\`int\`](https://www.postgresql.org/docs/current/datatype-numeric.html).
108
+ - scalar [\`Int64\`](scalar.md#Int64) maps to [\`bigint\`](https://www.postgresql.org/docs/current/datatype-numeric.html).
109
+ - scalar [\`Float\`](scalar.md#Float) maps to [\`double precision\`](https://www.postgresql.org/docs/current/datatype-numeric.html).
110
+ - scalar [\`Boolean\`](scalar.md#Boolean) maps to [\`boolean\`](https://www.postgresql.org/docs/current/datatype-boolean.html).
111
+ - scalar [\`Date\`](scalar.md#Date) maps to [\`date\`](https://www.postgresql.org/docs/current/datatype-datetime.html).
112
+ - scalar [\`Timestamp\`](scalar.md#Timestamp) maps to [\`timestamptz\`](https://www.postgresql.org/docs/current/datatype-datetime.html).
113
+ - scalar [\`Any\`](scalar.md#Any) maps to [\`jsonb\`](https://www.postgresql.org/docs/current/datatype-json.html).
114
+ - scalar [\`Vector\`](scalar.md#Vector) maps to [\`pgvector\`](https://github.com/pgvector/pgvector).
115
+
116
+ Array scalar fields are mapped to [Postgres arrays](https://www.postgresql.org/docs/current/arrays.html).
117
+
118
+ ###### Example: Serial Primary Key
119
+
120
+ For example, you can define auto-increment primary key.
121
+
122
+ \`\`\`graphql
123
+ type Post @table {
124
+ id: Int! @col(name: "post_id", dataType: "serial")
125
+ }
126
+ \`\`\`
127
+
128
+ Data Connect converts it to the following SQL table schema.
129
+
130
+ \`\`\`sql
131
+ CREATE TABLE "public"."post" (
132
+ "post_id" serial NOT NULL,
133
+ PRIMARY KEY ("id")
134
+ )
135
+ \`\`\`
136
+
137
+ ###### Example: Vector
138
+
139
+ \`\`\`graphql
140
+ type Post @table {
141
+ content: String! @col(name: "post_content")
142
+ contentEmbedding: Vector! @col(size:768)
143
+ }
144
+ \`\`\`
145
+
146
+ | Argument | Type | Description |
147
+ |---|---|---|
148
+ | \`name\` | [\`String\`](scalar.md#String) | The SQL database column name. Defaults to snake_case of the field name. |
149
+ | \`dataType\` | [\`String\`](scalar.md#String) | Configures the custom SQL data type. Each GraphQL type can map to multiple SQL data types. Refer to [Postgres supported data types](https://www.postgresql.org/docs/current/datatype.html). Incompatible SQL data type will lead to undefined behavior. |
150
+ | \`size\` | [\`Int\`](scalar.md#Int) | Required on [\`Vector\`](scalar.md#Vector) columns. It specifies the length of the Vector. \`textembedding-gecko@003\` model generates [\`Vector\`](scalar.md#Vector) of \`@col(size:768)\`. |
151
+
152
+ ### @default on \`FIELD_DEFINITION\` {:#default}
153
+ Specifies the default value for a column field.
154
+
155
+ For example:
156
+
157
+ \`\`\`graphql
158
+ type User @table(key: "uid") {
159
+ uid: String! @default(expr: "auth.uid")
160
+ number: Int! @col(dataType: "serial")
161
+ createdAt: Date! @default(expr: "request.time")
162
+ role: String! @default(value: "Member")
163
+ credit: Int! @default(value: 100)
164
+ }
165
+ \`\`\`
166
+
167
+ The supported arguments vary based on the field type.
168
+
169
+ | Argument | Type | Description |
170
+ |---|---|---|
171
+ | \`value\` | [\`Any\`](scalar.md#Any) | A constant value validated against the field's GraphQL type during compilation. |
172
+ | \`expr\` | [\`Any_Expr\`](scalar.md#Any_Expr) | A CEL expression whose return value must match the field's data type. |
173
+ | \`sql\` | [\`Any_SQL\`](scalar.md#Any_SQL) | A raw SQL expression, whose SQL data type must match the underlying column. The value is any variable-free expression (in particular, cross-references to other columns in the current table are not allowed). Subqueries are not allowed either. See [PostgreSQL defaults](https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-DEFAULT) for more details. |
174
+
175
+ ### @index on \`FIELD_DEFINITION\` | \`OBJECT\` {:#index}
176
+ Defines a database index to optimize query performance.
177
+
178
+ \`\`\`graphql
179
+ type User @table @index(fields: ["name", "phoneNumber"], order: [ASC, DESC]) {
180
+ name: String @index
181
+ phoneNumber: Int64 @index
182
+ tags: [String] @index # GIN Index
183
+ }
184
+ \`\`\`
185
+
186
+ ##### Single Field Index
187
+
188
+ You can put [\`@index\`](directive.md#index) on a [\`@col\`](directive.md#col) field to create a SQL index.
189
+
190
+ \`@index(order)\` matters little for single field indexes, as they can be scanned
191
+ in both directions.
192
+
193
+ ##### Composite Index
194
+
195
+ You can put \`@index(fields: [...])\` on [\`@table\`](directive.md#table) type to define composite indexes.
196
+
197
+ \`@index(order: [...])\` can customize the index order to satisfy particular
198
+ filter and order requirement.
199
+
200
+ | Argument | Type | Description |
201
+ |---|---|---|
202
+ | \`name\` | [\`String\`](scalar.md#String) | Configure the SQL database index id. If not overridden, Data Connect generates the index name: - \`{table_name}_{first_field}_{second_field}_aa_idx\` - \`{table_name}_{field_name}_idx\` |
203
+ | \`fields\` | [\`[String!]\`](scalar.md#String) | Only allowed and required when used on a [\`@table\`](directive.md#table) type. Specifies the fields to create the index on. |
204
+ | \`order\` | [\`[IndexFieldOrder!]\`](enum.md#IndexFieldOrder) | Only allowed for \`BTREE\` [\`@index\`](directive.md#index) on [\`@table\`](directive.md#table) type. Specifies the order for each indexed column. Defaults to all \`ASC\`. |
205
+ | \`type\` | [\`IndexType\`](enum.md#IndexType) | Customize the index type. For most index, it defaults to \`BTREE\`. For array fields, only allowed [\`IndexType\`](enum.md#IndexType) is \`GIN\`. For [\`Vector\`](scalar.md#Vector) fields, defaults to \`HNSW\`, may configure to \`IVFFLAT\`. |
206
+ | \`vector_method\` | [\`VectorSimilarityMethod\`](enum.md#VectorSimilarityMethod) | Only allowed when used on vector field. Defines the vector similarity method. Defaults to \`INNER_PRODUCT\`. |
207
+
208
+ ### @ref on \`FIELD_DEFINITION\` {:#ref}
209
+ Defines a foreign key reference to another table.
210
+
211
+ For example, we can define a many-to-one relation.
212
+
213
+ \`\`\`graphql
214
+ type ManyTable @table {
215
+ refField: OneTable!
216
+ }
217
+ type OneTable @table {
218
+ someField: String!
219
+ }
220
+ \`\`\`
221
+ Data Connect adds implicit foreign key column and relation query field. So the
222
+ above schema is equivalent to the following schema.
223
+
224
+ \`\`\`graphql
225
+ type ManyTable @table {
226
+ id: UUID! @default(expr: "uuidV4()")
227
+ refField: OneTable! @ref(fields: "refFieldId", references: "id")
228
+ refFieldId: UUID!
229
+ }
230
+ type OneTable @table {
231
+ id: UUID! @default(expr: "uuidV4()")
232
+ someField: UUID!
233
+ # Generated Fields:
234
+ # manyTables_on_refField: [ManyTable!]!
235
+ }
236
+ \`\`\`
237
+ Data Connect generates the necessary foreign key constraint.
238
+
239
+ \`\`\`sql
240
+ CREATE TABLE "public"."many_table" (
241
+ "id" uuid NOT NULL DEFAULT uuid_generate_v4(),
242
+ "ref_field_id" uuid NOT NULL,
243
+ PRIMARY KEY ("id"),
244
+ CONSTRAINT "many_table_ref_field_id_fkey" FOREIGN KEY ("ref_field_id") REFERENCES "public"."one_table" ("id") ON DELETE CASCADE
245
+ )
246
+ \`\`\`
247
+
248
+ ###### Example: Traverse the Reference Field
249
+
250
+ \`\`\`graphql
251
+ query ($id: UUID!) {
252
+ manyTable(id: $id) {
253
+ refField { id }
254
+ }
255
+ }
256
+ \`\`\`
257
+
258
+ ###### Example: Reverse Traverse the Reference field
259
+
260
+ \`\`\`graphql
261
+ query ($id: UUID!) {
262
+ oneTable(id: $id) {
263
+ manyTables_on_refField { id }
264
+ }
265
+ }
266
+ \`\`\`
267
+
268
+ ##### Optional Many-to-One Relation
269
+
270
+ An optional foreign key reference will be set to null if the referenced row is deleted.
271
+
272
+ In this example, if a \`User\` is deleted, the \`assignee\` and \`reporter\`
273
+ references will be set to null.
274
+
275
+ \`\`\`graphql
276
+ type Bug @table {
277
+ title: String!
278
+ assignee: User
279
+ reproter: User
280
+ }
281
+
282
+ type User @table { name: String! }
283
+ \`\`\`
284
+
285
+ ##### Required Many-to-One Relation
286
+
287
+ A required foreign key reference will cascade delete if the referenced row is
288
+ deleted.
289
+
290
+ In this example, if a \`Post\` is deleted, associated comments will also be
291
+ deleted.
292
+
293
+ \`\`\`graphql
294
+ type Comment @table {
295
+ post: Post!
296
+ content: String!
297
+ }
298
+
299
+ type Post @table { title: String! }
300
+ \`\`\`
301
+
302
+ ##### Many To Many Relation
303
+
304
+ You can define a many-to-many relation with a join table.
305
+
306
+ \`\`\`graphql
307
+ type Membership @table(key: ["group", "user"]) {
308
+ group: Group!
309
+ user: User!
310
+ role: String! @default(value: "member")
311
+ }
312
+
313
+ type Group @table { name: String! }
314
+ type User @table { name: String! }
315
+ \`\`\`
316
+
317
+ When Data Connect sees a table with two reference field as its primary key, it
318
+ knows this is a join table, so expands the many-to-many query field.
319
+
320
+ \`\`\`graphql
321
+ type Group @table {
322
+ name: String!
323
+ # Generated Fields:
324
+ # users_via_Membership: [User!]!
325
+ # memberships_on_group: [Membership!]!
326
+ }
327
+ type User @table {
328
+ name: String!
329
+ # Generated Fields:
330
+ # groups_via_Membership: [Group!]!
331
+ # memberships_on_user: [Membership!]!
332
+ }
333
+ \`\`\`
334
+
335
+ ###### Example: Traverse the Many-To-Many Relation
336
+
337
+ \`\`\`graphql
338
+ query ($id: UUID!) {
339
+ group(id: $id) {
340
+ users: users_via_Membership {
341
+ name
342
+ }
343
+ }
344
+ }
345
+ \`\`\`
346
+
347
+ ###### Example: Traverse to the Join Table
348
+
349
+ \`\`\`graphql
350
+ query ($id: UUID!) {
351
+ group(id: $id) {
352
+ memberships: memberships_on_group {
353
+ user { name }
354
+ role
355
+ }
356
+ }
357
+ }
358
+ \`\`\`
359
+
360
+ ##### One To One Relation
361
+
362
+ You can even define a one-to-one relation with the help of [\`@unique\`](directive.md#unique) or \`@table(key)\`.
363
+
364
+ \`\`\`graphql
365
+ type User @table {
366
+ name: String
367
+ }
368
+ type Account @table {
369
+ user: User! @unique
370
+ }
371
+ # Alternatively, use primary key constraint.
372
+ # type Account @table(key: "user") {
373
+ # user: User!
374
+ # }
375
+ \`\`\`
376
+
377
+ ###### Example: Transerse the Reference Field
378
+
379
+ \`\`\`graphql
380
+ query ($id: UUID!) {
381
+ account(id: $id) {
382
+ user { id }
383
+ }
384
+ }
385
+ \`\`\`
386
+
387
+ ###### Example: Reverse Traverse the Reference field
388
+
389
+ \`\`\`graphql
390
+ query ($id: UUID!) {
391
+ user(id: $id) {
392
+ account_on_user { id }
393
+ }
394
+ }
395
+ \`\`\`
396
+
397
+ ##### Customizations
398
+
399
+ - \`@ref(constraintName)\` can customize the SQL foreign key constraint name (\`table_name_ref_field_fkey\` above).
400
+ - \`@ref(fields)\` can customize the foreign key field names.
401
+ - \`@ref(references)\` can customize the constraint to reference other columns.
402
+ By default, \`@ref(references)\` is the primary key of the [\`@ref\`](directive.md#ref) table.
403
+ Other fields with [\`@unique\`](directive.md#unique) may also be referred in the foreign key constraint.
404
+
405
+ | Argument | Type | Description |
406
+ |---|---|---|
407
+ | \`constraintName\` | [\`String\`](scalar.md#String) | The SQL database foreign key constraint name. Defaults to snake_case \`{table_name}_{field_name}_fkey\`. |
408
+ | \`fields\` | [\`[String!]\`](scalar.md#String) | Foreign key fields. Defaults to \`{tableName}{PrimaryIdName}\`. |
409
+ | \`references\` | [\`[String!]\`](scalar.md#String) | The fields that the foreign key references in the other table. Defaults to its primary key. |
410
+
411
+ ### @table on \`OBJECT\` {:#table}
412
+ Defines a relational database table.
413
+
414
+ In this example, we defined one table with a field named \`myField\`.
415
+
416
+ \`\`\`graphql
417
+ type TableName @table {
418
+ myField: String
419
+ }
420
+ \`\`\`
421
+ Data Connect adds an implicit \`id\` primary key column. So the above schema is equivalent to:
422
+
423
+ \`\`\`graphql
424
+ type TableName @table(key: "id") {
425
+ id: String @default(expr: "uuidV4()")
426
+ myField: String
427
+ }
428
+ \`\`\`
429
+
430
+ Data Connect generates the following SQL table and CRUD operations to use it.
431
+
432
+ \`\`\`sql
433
+ CREATE TABLE "public"."table_name" (
434
+ "id" uuid NOT NULL DEFAULT uuid_generate_v4(),
435
+ "my_field" text NULL,
436
+ PRIMARY KEY ("id")
437
+ )
438
+ \`\`\`
439
+
440
+ * You can lookup a row: \`query ($id: UUID!) { tableName(id: $id) { myField } } \`
441
+ * You can find rows using: \`query tableNames(limit: 20) { myField }\`
442
+ * You can insert a row: \`mutation { tableName_insert(data: {myField: "foo"}) }\`
443
+ * You can update a row: \`mutation ($id: UUID!) { tableName_update(id: $id, data: {myField: "bar"}) }\`
444
+ * You can delete a row: \`mutation ($id: UUID!) { tableName_delete(id: $id) }\`
445
+
446
+ ##### Customizations
447
+
448
+ - \`@table(singular)\` and \`@table(plural)\` can customize the singular and plural name.
449
+ - \`@table(name)\` can customize the Postgres table name.
450
+ - \`@table(key)\` can customize the primary key field name and type.
451
+
452
+ For example, the \`User\` table often has a \`uid\` as its primary key.
453
+
454
+ \`\`\`graphql
455
+ type User @table(key: "uid") {
456
+ uid: String!
457
+ name: String
458
+ }
459
+ \`\`\`
460
+
461
+ * You can securely lookup a row: \`query { user(key: {uid_expr: "auth.uid"}) { name } } \`
462
+ * You can securely insert a row: \`mutation { user_insert(data: {uid_expr: "auth.uid" name: "Fred"}) }\`
463
+ * You can securely update a row: \`mutation { user_update(key: {uid_expr: "auth.uid"}, data: {name: "New Name"}) }\`
464
+ * You can securely delete a row: \`mutation { user_delete(key: {uid_expr: "auth.uid"}) }\`
465
+
466
+ [\`@table\`](directive.md#table) type can be configured further with:
467
+
468
+ - Custom SQL data types for columns. See [\`@col\`](directive.md#col).
469
+ - Add SQL indexes. See [\`@index\`](directive.md#index).
470
+ - Add SQL unique constraints. See [\`@unique\`](directive.md#unique).
471
+ - Add foreign key constraints to define relations. See [\`@ref\`](directive.md#ref).
472
+
473
+ | Argument | Type | Description |
474
+ |---|---|---|
475
+ | \`name\` | [\`String\`](scalar.md#String) | Configures the SQL database table name. Defaults to snake_case like \`table_name\`. |
476
+ | \`singular\` | [\`String\`](scalar.md#String) | Configures the singular name. Defaults to the camelCase like \`tableName\`. |
477
+ | \`plural\` | [\`String\`](scalar.md#String) | Configures the plural name. Defaults to infer based on English plural pattern like \`tableNames\`. |
478
+ | \`key\` | [\`[String!]\`](scalar.md#String) | Defines the primary key of the table. Defaults to a single field named \`id\`. If not present already, Data Connect adds an implicit field \`id: UUID! @default(expr: "uuidV4()")\`. |
479
+
480
+ ### @unique on \`FIELD_DEFINITION\` | \`OBJECT\` {:#unique}
481
+ Defines unique constraints on [\`@table\`](directive.md#table).
482
+
483
+ For example,
484
+
485
+ \`\`\`graphql
486
+ type User @table {
487
+ phoneNumber: Int64 @unique
488
+ }
489
+ type UserProfile @table {
490
+ user: User! @unique
491
+ address: String @unique
492
+ }
493
+ \`\`\`
494
+
495
+ - [\`@unique\`](directive.md#unique) on a [\`@col\`](directive.md#col) field adds a single-column unique constraint.
496
+ - [\`@unique\`](directive.md#unique) on a [\`@table\`](directive.md#table) type adds a composite unique constraint.
497
+ - [\`@unique\`](directive.md#unique) on a [\`@ref\`](directive.md#ref) defines a one-to-one relation. It adds unique constraint
498
+ on \`@ref(fields)\`.
499
+
500
+ [\`@unique\`](directive.md#unique) ensures those fields can uniquely identify a row, so other [\`@table\`](directive.md#table)
501
+ type may define \`@ref(references)\` to refer to fields that has a unique constraint.
502
+
503
+ | Argument | Type | Description |
504
+ |---|---|---|
505
+ | \`indexName\` | [\`String\`](scalar.md#String) | Configures the SQL database unique constraint name. If not overridden, Data Connect generates the unique constraint name: - \`table_name_first_field_second_field_uidx\` - \`table_name_only_field_name_uidx\` |
506
+ | \`fields\` | [\`[String!]\`](scalar.md#String) | Only allowed and required when used on OBJECT, this specifies the fields to create a unique constraint on. |
507
+
508
+ ### @view on \`OBJECT\` {:#view}
509
+ Defines a relational database Raw SQLview.
510
+
511
+ Data Connect generates GraphQL queries with WHERE and ORDER BY clauses.
512
+ However, not all SQL features has native GraphQL equivalent.
513
+
514
+ You can write **an arbitrary SQL SELECT statement**. Data Connect
515
+ would map Graphql fields on [\`@view\`](directive.md#view) type to columns in your SELECT statement.
516
+
517
+ * Scalar GQL fields (camelCase) should match a SQL column (snake_case)
518
+ in the SQL SELECT statement.
519
+ * Reference GQL field can point to another [\`@table\`](directive.md#table) type. Similar to foreign key
520
+ defined with [\`@ref\`](directive.md#ref) on a [\`@table\`](directive.md#table) type, a [\`@view\`](directive.md#view) type establishes a relation
521
+ when \`@ref(fields)\` match \`@ref(references)\` on the target table.
522
+
523
+ In this example, you can use \`@view(sql)\` to define an aggregation view on existing
524
+ table.
525
+
526
+ \`\`\`graphql
527
+ type User @table {
528
+ name: String
529
+ score: Int
530
+ }
531
+ type UserAggregation @view(sql: """
532
+ SELECT
533
+ COUNT(*) as count,
534
+ SUM(score) as sum,
535
+ AVG(score) as average,
536
+ PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY score) AS median,
537
+ (SELECT id FROM "user" LIMIT 1) as example_id
538
+ FROM "user"
539
+ """) {
540
+ count: Int
541
+ sum: Int
542
+ average: Float
543
+ median: Float
544
+ example: User
545
+ exampleId: UUID
546
+ }
547
+ \`\`\`
548
+
549
+ ###### Example: Query Raw SQL View
550
+
551
+ \`\`\`graphql
552
+ query {
553
+ userAggregations {
554
+ count sum average median
555
+ exampleId example { id }
556
+ }
557
+ }
558
+ \`\`\`
559
+
560
+ ##### One-to-One View
561
+
562
+ An one-to-one companion [\`@view\`](directive.md#view) can be handy if you want to argument a [\`@table\`](directive.md#table)
563
+ with additional implied content.
564
+
565
+ \`\`\`graphql
566
+ type Restaurant @table {
567
+ name: String!
568
+ }
569
+ type Review @table {
570
+ restaurant: Restaurant!
571
+ rating: Int!
572
+ }
573
+ type RestaurantStats @view(sql: """
574
+ SELECT
575
+ restaurant_id,
576
+ COUNT(*) AS review_count,
577
+ AVG(rating) AS average_rating
578
+ FROM review
579
+ GROUP BY restaurant_id
580
+ """) {
581
+ restaurant: Restaurant @unique
582
+ reviewCount: Int
583
+ averageRating: Float
584
+ }
585
+ \`\`\`
586
+
587
+ In this example, [\`@unique\`](directive.md#unique) convey the assumption that each \`Restaurant\` should
588
+ have only one \`RestaurantStats\` object.
589
+
590
+ ###### Example: Query One-to-One View
591
+
592
+ \`\`\`graphql
593
+ query ListRestaurants {
594
+ restaurants {
595
+ name
596
+ stats: restaurantStats_on_restaurant {
597
+ reviewCount
598
+ averageRating
599
+ }
600
+ }
601
+ }
602
+ \`\`\`
603
+
604
+ ###### Example: Filter based on One-to-One View
605
+
606
+ \`\`\`graphql
607
+ query BestRestaurants($minAvgRating: Float, $minReviewCount: Int) {
608
+ restaurants(where: {
609
+ restaurantStats_on_restaurant: {
610
+ averageRating: {ge: $minAvgRating}
611
+ reviewCount: {ge: $minReviewCount}
612
+ }
613
+ }) { name }
614
+ }
615
+ \`\`\`
616
+
617
+ ##### Customizations
618
+
619
+ - One of \`@view(sql)\` or \`@view(name)\` should be defined.
620
+ \`@view(name)\` can refer to a persisted SQL view in the Postgres schema.
621
+ - \`@view(singular)\` and \`@view(plural)\` can customize the singular and plural name.
622
+
623
+ [\`@view\`](directive.md#view) type can be configured further:
624
+
625
+ - [\`@unique\`](directive.md#unique) lets you define one-to-one relation.
626
+ - [\`@col\`](directive.md#col) lets you customize SQL column mapping. For example, \`@col(name: "column_in_select")\`.
627
+
628
+ ##### Limitations
629
+
630
+ Raw SQL view doesn't have a primary key, so it doesn't support lookup. Other
631
+ [\`@table\`](directive.md#table) or [\`@view\`](directive.md#view) cannot have [\`@ref\`](directive.md#ref) to a view either.
632
+
633
+ View cannot be mutated. You can perform CRUD operations on the underlying
634
+ table to alter its content.
635
+
636
+ **Important: Data Connect doesn't parse and validate SQL**
637
+
638
+ - If the SQL view is invalid or undefined, related requests may fail.
639
+ - If the SQL view return incompatible types. Firebase Data Connect may surface
640
+ errors.
641
+ - If a field doesn't have a corresponding column in the SQL SELECT statement,
642
+ it will always be \`null\`.
643
+ - There is no way to ensure VIEW to TABLE [\`@ref\`](directive.md#ref) constraint.
644
+ - All fields must be nullable in case they aren't found in the SELECT statement
645
+ or in the referenced table.
646
+
647
+ **Important: You should always test [\`@view\`](directive.md#view)!**
648
+
649
+ | Argument | Type | Description |
650
+ |---|---|---|
651
+ | \`name\` | [\`String\`](scalar.md#String) | The SQL view name. If neither \`name\` nor \`sql\` are provided, defaults to the snake_case of the singular type name. \`name\` and \`sql\` cannot be specified at the same time. |
652
+ | \`sql\` | [\`String\`](scalar.md#String) | SQL \`SELECT\` statement used as the basis for this type. SQL SELECT columns should use snake_case. GraphQL fields should use camelCase. \`name\` and \`sql\` cannot be specified at the same time. |
653
+ | \`singular\` | [\`String\`](scalar.md#String) | Configures the singular name. Defaults to the camelCase like \`viewName\`. |
654
+ | \`plural\` | [\`String\`](scalar.md#String) | Configures the plural name. Defaults to infer based on English plural pattern like \`viewNames\`. |
655
+ `.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "14.15.0",
3
+ "version": "14.15.1",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
File without changes