firebase-tools 14.4.0 → 14.5.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/lib/apphosting/backend.js +2 -0
- package/lib/bin/mcp.js +1 -0
- package/lib/commands/init.js +0 -3
- package/lib/config.js +42 -24
- package/lib/dataconnect/cloudAICompanionClient.js +7 -2
- package/lib/dataconnect/cloudAICompanionTypes.js +2 -0
- package/lib/dataconnect/fileUtils.js +11 -4
- package/lib/dataconnect/schemaMigration.js +6 -7
- package/lib/deploy/apphosting/deploy.js +3 -0
- package/lib/deploy/apphosting/prepare.js +34 -28
- package/lib/deploy/apphosting/release.js +3 -0
- package/lib/deploy/firestore/deploy.js +47 -4
- package/lib/emulator/apphosting/serve.js +1 -1
- package/lib/emulator/downloadableEmulatorInfo.json +39 -18
- package/lib/emulator/downloadableEmulators.js +15 -59
- package/lib/extensions/manifest.js +0 -3
- package/lib/frameworks/angular/index.js +1 -1
- package/lib/frameworks/angular/utils.js +17 -6
- package/lib/gcp/apphosting.js +13 -1
- package/lib/gcp/run.js +19 -1
- package/lib/init/features/apphosting.js +3 -2
- package/lib/init/features/database.js +11 -19
- package/lib/init/features/dataconnect/index.js +27 -26
- package/lib/init/features/dataconnect/sdk.js +19 -6
- package/lib/init/features/emulators.js +4 -3
- package/lib/init/features/firestore/index.js +44 -34
- package/lib/init/features/firestore/indexes.js +12 -13
- package/lib/init/features/firestore/rules.js +8 -15
- package/lib/init/features/genkit/index.js +16 -9
- package/lib/init/features/hosting/index.js +9 -8
- package/lib/init/features/index.js +3 -2
- package/lib/init/features/storage.js +31 -8
- package/lib/init/index.js +5 -1
- package/lib/mcp/index.js +10 -6
- package/lib/mcp/tool.js +2 -1
- package/lib/mcp/tools/apphosting/fetch_logs.js +69 -0
- package/lib/mcp/tools/apphosting/index.js +6 -0
- package/lib/mcp/tools/apphosting/list_backends.js +51 -0
- package/lib/mcp/tools/core/index.js +2 -0
- package/lib/mcp/tools/core/init.js +26 -2
- package/lib/mcp/tools/core/list_apps.js +10 -5
- package/lib/mcp/tools/core/list_projects.js +45 -0
- package/lib/mcp/tools/dataconnect/generate_operation.js +1 -1
- package/lib/mcp/tools/firestore/query_collection.js +13 -7
- package/lib/mcp/tools/index.js +2 -0
- package/lib/mcp/tools/storage/get_download_url.js +1 -1
- package/lib/mcp/types.js +1 -0
- package/lib/mcp/util.js +137 -1
- package/lib/mcp/util.test.js +468 -0
- package/lib/prompt.js +1 -1
- package/lib/track.js +1 -1
- package/package.json +1 -1
- package/schema/connector-yaml.json +12 -0
- package/schema/extension-yaml.json +17 -4
- package/schema/firebase-config.json +3 -0
- package/standalone/package.json +1 -1
- package/templates/dataconnect-prompts/operation-generation-cursor-windsurf-rule.txt +273 -0
- package/templates/dataconnect-prompts/schema-generation-cursor-windsurf-rule.txt +653 -0
- package/templates/genkit/firebase.1.0.0.template +5 -0
- package/templates/init/firestore/firestore.rules +2 -0
- package/templates/init/functions/typescript/package.lint.json +1 -1
- package/templates/init/functions/typescript/package.nolint.json +1 -1
package/lib/mcp/util.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.checkFeatureActive = exports.commandExistsSync = exports.mcpError = exports.toContent = void 0;
|
|
3
|
+
exports.cleanSchema = exports.checkFeatureActive = exports.commandExistsSync = exports.mcpError = exports.toContent = void 0;
|
|
4
4
|
const child_process_1 = require("child_process");
|
|
5
5
|
const js_yaml_1 = require("js-yaml");
|
|
6
6
|
const os_1 = require("os");
|
|
@@ -62,6 +62,7 @@ const SERVER_FEATURE_APIS = {
|
|
|
62
62
|
messaging: (0, api_1.messagingApiOrigin)(),
|
|
63
63
|
remoteconfig: (0, api_1.remoteConfigApiOrigin)(),
|
|
64
64
|
crashlytics: (0, api_1.crashlyticsApiOrigin)(),
|
|
65
|
+
apphosting: (0, api_1.apphostingOrigin)(),
|
|
65
66
|
};
|
|
66
67
|
async function checkFeatureActive(feature, projectId, options) {
|
|
67
68
|
var _a;
|
|
@@ -77,3 +78,138 @@ async function checkFeatureActive(feature, projectId, options) {
|
|
|
77
78
|
return false;
|
|
78
79
|
}
|
|
79
80
|
exports.checkFeatureActive = checkFeatureActive;
|
|
81
|
+
function deepClean(obj, isRootLevel = false) {
|
|
82
|
+
if (typeof obj !== "object" || obj === null) {
|
|
83
|
+
return obj;
|
|
84
|
+
}
|
|
85
|
+
const cleanedObj = Object.assign({}, obj);
|
|
86
|
+
if (cleanedObj.hasOwnProperty("$schema")) {
|
|
87
|
+
delete cleanedObj.$schema;
|
|
88
|
+
}
|
|
89
|
+
if (cleanedObj.hasOwnProperty("additionalProperties")) {
|
|
90
|
+
delete cleanedObj.additionalProperties;
|
|
91
|
+
}
|
|
92
|
+
if (cleanedObj.hasOwnProperty("type")) {
|
|
93
|
+
const currentType = cleanedObj.type;
|
|
94
|
+
if (Array.isArray(currentType)) {
|
|
95
|
+
let filteredTypes = currentType.filter((t) => t !== "null");
|
|
96
|
+
if (isRootLevel) {
|
|
97
|
+
filteredTypes = filteredTypes.filter((t) => t !== "array");
|
|
98
|
+
}
|
|
99
|
+
if (filteredTypes.length === 0) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
else if (filteredTypes.length === 1) {
|
|
103
|
+
cleanedObj.type = filteredTypes[0];
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
delete cleanedObj.type;
|
|
107
|
+
cleanedObj.anyOf = filteredTypes
|
|
108
|
+
.map((t) => {
|
|
109
|
+
return deepClean({ type: t }, false);
|
|
110
|
+
})
|
|
111
|
+
.filter((subSchema) => subSchema !== null);
|
|
112
|
+
if (cleanedObj.anyOf.length === 0) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
if (cleanedObj.anyOf.length === 1) {
|
|
116
|
+
const singleSchema = cleanedObj.anyOf[0];
|
|
117
|
+
delete cleanedObj.anyOf;
|
|
118
|
+
Object.assign(cleanedObj, singleSchema);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else if (typeof currentType === "string") {
|
|
123
|
+
if (currentType === "null") {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
if (isRootLevel && currentType === "array") {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (cleanedObj.hasOwnProperty("properties") &&
|
|
132
|
+
typeof cleanedObj.properties === "object" &&
|
|
133
|
+
cleanedObj.properties !== null) {
|
|
134
|
+
const newProperties = {};
|
|
135
|
+
for (const key in cleanedObj.properties) {
|
|
136
|
+
if (cleanedObj.properties.hasOwnProperty(key)) {
|
|
137
|
+
const cleanedPropertySchema = deepClean(cleanedObj.properties[key], false);
|
|
138
|
+
if (cleanedPropertySchema !== null) {
|
|
139
|
+
newProperties[key] = cleanedPropertySchema;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (Object.keys(newProperties).length === 0) {
|
|
144
|
+
delete cleanedObj.properties;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
cleanedObj.properties = newProperties;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (cleanedObj.hasOwnProperty("items") &&
|
|
151
|
+
typeof cleanedObj.items === "object" &&
|
|
152
|
+
cleanedObj.items !== null) {
|
|
153
|
+
const cleanedItemsSchema = deepClean(cleanedObj.items, false);
|
|
154
|
+
if (cleanedItemsSchema === null) {
|
|
155
|
+
delete cleanedObj.items;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
cleanedObj.items = cleanedItemsSchema;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const defKeywords = ["$defs", "definitions"];
|
|
162
|
+
for (const keyword of defKeywords) {
|
|
163
|
+
if (cleanedObj.hasOwnProperty(keyword) &&
|
|
164
|
+
typeof cleanedObj[keyword] === "object" &&
|
|
165
|
+
cleanedObj[keyword] !== null) {
|
|
166
|
+
const newDefs = {};
|
|
167
|
+
for (const defKey in cleanedObj[keyword]) {
|
|
168
|
+
if (cleanedObj[keyword].hasOwnProperty(defKey)) {
|
|
169
|
+
const cleanedDef = deepClean(cleanedObj[keyword][defKey], false);
|
|
170
|
+
if (cleanedDef !== null) {
|
|
171
|
+
newDefs[defKey] = cleanedDef;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (Object.keys(newDefs).length === 0) {
|
|
176
|
+
delete cleanedObj[keyword];
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
cleanedObj[keyword] = newDefs;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const schemaArrayKeywords = ["anyOf", "allOf", "oneOf"];
|
|
184
|
+
for (const keyword of schemaArrayKeywords) {
|
|
185
|
+
if (cleanedObj.hasOwnProperty(keyword) && Array.isArray(cleanedObj[keyword])) {
|
|
186
|
+
const newSchemaArray = cleanedObj[keyword]
|
|
187
|
+
.map((subSchema) => deepClean(subSchema, false))
|
|
188
|
+
.filter((subSchema) => subSchema !== null);
|
|
189
|
+
if (newSchemaArray.length === 0) {
|
|
190
|
+
delete cleanedObj[keyword];
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
cleanedObj[keyword] = newSchemaArray;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return cleanedObj;
|
|
198
|
+
}
|
|
199
|
+
function cleanSchema(schema) {
|
|
200
|
+
if (schema && schema.hasOwnProperty("type")) {
|
|
201
|
+
const topLevelType = schema.type;
|
|
202
|
+
if (topLevelType === "array") {
|
|
203
|
+
return {};
|
|
204
|
+
}
|
|
205
|
+
if (Array.isArray(topLevelType)) {
|
|
206
|
+
const filteredRootTypes = topLevelType.filter((t) => t !== "null" && t !== "array");
|
|
207
|
+
if (filteredRootTypes.length === 0 && topLevelType.includes("array")) {
|
|
208
|
+
return {};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const result = deepClean(schema, true);
|
|
213
|
+
return result === null ? {} : result;
|
|
214
|
+
}
|
|
215
|
+
exports.cleanSchema = cleanSchema;
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chai_1 = require("chai");
|
|
4
|
+
const util_1 = require("./util");
|
|
5
|
+
const testCases = [
|
|
6
|
+
{
|
|
7
|
+
desc: "should remove $schema property",
|
|
8
|
+
input: {
|
|
9
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: { name: { type: "string" } },
|
|
12
|
+
},
|
|
13
|
+
expected: {
|
|
14
|
+
type: "object",
|
|
15
|
+
properties: { name: { type: "string" } },
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
desc: "should remove additionalProperties field",
|
|
20
|
+
input: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: { name: { type: "string" } },
|
|
23
|
+
additionalProperties: false,
|
|
24
|
+
},
|
|
25
|
+
expected: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: { name: { type: "string" } },
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
desc: "should remove additionalProperties from nested objects",
|
|
32
|
+
input: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
user: {
|
|
36
|
+
type: "object",
|
|
37
|
+
properties: { id: { type: "number" } },
|
|
38
|
+
additionalProperties: true,
|
|
39
|
+
},
|
|
40
|
+
meta: {
|
|
41
|
+
type: "object",
|
|
42
|
+
additionalProperties: { type: "string" },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
additionalProperties: false,
|
|
46
|
+
},
|
|
47
|
+
expected: {
|
|
48
|
+
type: "object",
|
|
49
|
+
properties: {
|
|
50
|
+
user: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: { id: { type: "number" } },
|
|
53
|
+
},
|
|
54
|
+
meta: {
|
|
55
|
+
type: "object",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
desc: "should remove top-level array type (string)",
|
|
62
|
+
input: { type: "array", items: { type: "string" } },
|
|
63
|
+
expected: {},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
desc: "should remove top-level array type (array of types including array)",
|
|
67
|
+
input: { type: ["array", "string"], items: { type: "string" } },
|
|
68
|
+
expected: { type: "string", items: { type: "string" } },
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
desc: "should remove top-level array type (array of types including array and null)",
|
|
72
|
+
input: { type: ["array", "null"], items: { type: "string" } },
|
|
73
|
+
expected: {},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
desc: "should remove top-level null type",
|
|
77
|
+
input: { type: "null" },
|
|
78
|
+
expected: {},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
desc: "should KEEP array type in properties",
|
|
82
|
+
input: {
|
|
83
|
+
type: "object",
|
|
84
|
+
properties: {
|
|
85
|
+
tags: { type: "array", items: { type: "string" } },
|
|
86
|
+
name: { type: "string" },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
expected: {
|
|
90
|
+
type: "object",
|
|
91
|
+
properties: {
|
|
92
|
+
tags: { type: "array", items: { type: "string" } },
|
|
93
|
+
name: { type: "string" },
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
desc: "should remove null type from properties",
|
|
99
|
+
input: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
optionalField: { type: "null" },
|
|
103
|
+
name: { type: "string" },
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
expected: {
|
|
107
|
+
type: "object",
|
|
108
|
+
properties: { name: { type: "string" } },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
desc: "should convert type: ['string', 'null', 'array'] to anyOf: [{type: 'string'}, {type: 'array'}] in properties",
|
|
113
|
+
input: {
|
|
114
|
+
type: "object",
|
|
115
|
+
properties: {
|
|
116
|
+
mixed: { type: ["string", "null", "array"] },
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
expected: {
|
|
120
|
+
type: "object",
|
|
121
|
+
properties: {
|
|
122
|
+
mixed: { anyOf: [{ type: "string" }, { type: "array" }] },
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
desc: "should convert type: ['string', 'number', 'null', 'array'] to anyOf in properties",
|
|
128
|
+
input: {
|
|
129
|
+
type: "object",
|
|
130
|
+
properties: {
|
|
131
|
+
mixed: { type: ["string", "number", "null", "array"] },
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
expected: {
|
|
135
|
+
type: "object",
|
|
136
|
+
properties: {
|
|
137
|
+
mixed: { anyOf: [{ type: "string" }, { type: "number" }, { type: "array" }] },
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
desc: "should simplify type: ['string', 'null'] to type: 'string' in properties",
|
|
143
|
+
input: {
|
|
144
|
+
type: "object",
|
|
145
|
+
properties: {
|
|
146
|
+
simpleMixed: { type: ["string", "null"] },
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
expected: {
|
|
150
|
+
type: "object",
|
|
151
|
+
properties: {
|
|
152
|
+
simpleMixed: { type: "string" },
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
desc: "should remove property if its type array becomes empty after filtering (e.g. only null)",
|
|
158
|
+
input: {
|
|
159
|
+
type: "object",
|
|
160
|
+
properties: {
|
|
161
|
+
onlyNull: { type: ["null"] },
|
|
162
|
+
name: { type: "string" },
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
expected: {
|
|
166
|
+
type: "object",
|
|
167
|
+
properties: { name: { type: "string" } },
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
desc: "should keep property if its type array contains only 'array' (not root) and simplify",
|
|
172
|
+
input: {
|
|
173
|
+
type: "object",
|
|
174
|
+
properties: {
|
|
175
|
+
onlyArray: { type: ["array", "null"] },
|
|
176
|
+
name: { type: "string" },
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
expected: {
|
|
180
|
+
type: "object",
|
|
181
|
+
properties: {
|
|
182
|
+
onlyArray: { type: "array" },
|
|
183
|
+
name: { type: "string" },
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
desc: "should handle nested objects and clean them (arrays kept in nested, type arrays become anyOf or simplified)",
|
|
189
|
+
input: {
|
|
190
|
+
type: "object",
|
|
191
|
+
properties: {
|
|
192
|
+
user: {
|
|
193
|
+
type: "object",
|
|
194
|
+
properties: {
|
|
195
|
+
id: { type: "number" },
|
|
196
|
+
tags: { type: "array", items: { type: "string" } },
|
|
197
|
+
status: { type: ["string", "integer", "null"] },
|
|
198
|
+
maybeName: { type: ["string", "null"] },
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
expected: {
|
|
204
|
+
type: "object",
|
|
205
|
+
properties: {
|
|
206
|
+
user: {
|
|
207
|
+
type: "object",
|
|
208
|
+
properties: {
|
|
209
|
+
id: { type: "number" },
|
|
210
|
+
tags: { type: "array", items: { type: "string" } },
|
|
211
|
+
status: { anyOf: [{ type: "string" }, { type: "integer" }] },
|
|
212
|
+
maybeName: { type: "string" },
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
desc: "should remove items if its schema becomes null",
|
|
220
|
+
input: {
|
|
221
|
+
type: "object",
|
|
222
|
+
properties: {
|
|
223
|
+
someObjectWithItems: {
|
|
224
|
+
type: "object",
|
|
225
|
+
items: { type: "null" },
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
expected: {
|
|
230
|
+
type: "object",
|
|
231
|
+
properties: {
|
|
232
|
+
someObjectWithItems: {
|
|
233
|
+
type: "object",
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
desc: "should clean definitions ($defs), convert type arrays to anyOf/simplified",
|
|
240
|
+
input: {
|
|
241
|
+
type: "object",
|
|
242
|
+
properties: {
|
|
243
|
+
myDef: { $ref: "#/$defs/invalidDef" },
|
|
244
|
+
myValidDef: { $ref: "#/$defs/validDefWithArray" },
|
|
245
|
+
myComplexDef: { $ref: "#/$defs/complexDef" },
|
|
246
|
+
},
|
|
247
|
+
$defs: {
|
|
248
|
+
invalidDef: { type: "null" },
|
|
249
|
+
validDef: { type: "string" },
|
|
250
|
+
validDefWithArray: { type: "array", items: { type: "number" } },
|
|
251
|
+
complexDef: { type: ["boolean", "string", "null"] },
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
expected: {
|
|
255
|
+
type: "object",
|
|
256
|
+
properties: {
|
|
257
|
+
myDef: { $ref: "#/$defs/invalidDef" },
|
|
258
|
+
myValidDef: { $ref: "#/$defs/validDefWithArray" },
|
|
259
|
+
myComplexDef: { $ref: "#/$defs/complexDef" },
|
|
260
|
+
},
|
|
261
|
+
$defs: {
|
|
262
|
+
validDef: { type: "string" },
|
|
263
|
+
validDefWithArray: { type: "array", items: { type: "number" } },
|
|
264
|
+
complexDef: { anyOf: [{ type: "boolean" }, { type: "string" }] },
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
desc: "should remove $defs if all definitions become invalid (e.g. all null)",
|
|
270
|
+
input: {
|
|
271
|
+
type: "object",
|
|
272
|
+
$defs: {
|
|
273
|
+
invalidDef1: { type: "null" },
|
|
274
|
+
invalidDef2: { type: "null" },
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
expected: {
|
|
278
|
+
type: "object",
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
desc: "should clean schema arrays (anyOf, allOf, oneOf), keep nested arrays, convert internal type arrays",
|
|
283
|
+
input: {
|
|
284
|
+
anyOf: [
|
|
285
|
+
{ type: "string" },
|
|
286
|
+
{ type: "array", items: { type: "number" } },
|
|
287
|
+
{ type: "null" },
|
|
288
|
+
{ type: ["integer", "boolean", "null"] },
|
|
289
|
+
],
|
|
290
|
+
allOf: [{ type: "number" }],
|
|
291
|
+
oneOf: [{ type: "boolean" }, { type: ["null", "array"] }],
|
|
292
|
+
},
|
|
293
|
+
expected: {
|
|
294
|
+
anyOf: [
|
|
295
|
+
{ type: "string" },
|
|
296
|
+
{ type: "array", items: { type: "number" } },
|
|
297
|
+
{ anyOf: [{ type: "integer" }, { type: "boolean" }] },
|
|
298
|
+
],
|
|
299
|
+
allOf: [{ type: "number" }],
|
|
300
|
+
oneOf: [{ type: "boolean" }, { type: "array" }],
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
desc: "should remove schema array keywords if their arrays become empty (e.g. all null)",
|
|
305
|
+
input: {
|
|
306
|
+
anyOf: [{ type: "null" }, { type: "null" }],
|
|
307
|
+
description: "test",
|
|
308
|
+
},
|
|
309
|
+
expected: {
|
|
310
|
+
description: "test",
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
desc: "should return an empty object if the entire schema is just { type: 'array' }",
|
|
315
|
+
input: { type: "array" },
|
|
316
|
+
expected: {},
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
desc: "should return an empty object if the entire schema is just { type: 'null' }",
|
|
320
|
+
input: { type: "null" },
|
|
321
|
+
expected: {},
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
desc: "should return an empty object if the entire schema is { type: ['null', 'array'] }",
|
|
325
|
+
input: { type: ["null", "array"] },
|
|
326
|
+
expected: {},
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
desc: "should not modify a schema that is already clean (with nested array and anyOf)",
|
|
330
|
+
input: {
|
|
331
|
+
type: "object",
|
|
332
|
+
properties: {
|
|
333
|
+
name: { type: "string" },
|
|
334
|
+
age: { type: "integer" },
|
|
335
|
+
scores: { type: "array", items: { type: "number" } },
|
|
336
|
+
choice: { anyOf: [{ type: "string" }, { type: "boolean" }] },
|
|
337
|
+
},
|
|
338
|
+
required: ["name"],
|
|
339
|
+
},
|
|
340
|
+
expected: {
|
|
341
|
+
type: "object",
|
|
342
|
+
properties: {
|
|
343
|
+
name: { type: "string" },
|
|
344
|
+
age: { type: "integer" },
|
|
345
|
+
scores: { type: "array", items: { type: "number" } },
|
|
346
|
+
choice: { anyOf: [{ type: "string" }, { type: "boolean" }] },
|
|
347
|
+
},
|
|
348
|
+
required: ["name"],
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
desc: "should handle deeply nested structures with various cleaning needs (arrays kept if not root, type arrays to anyOf)",
|
|
353
|
+
input: {
|
|
354
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
355
|
+
title: "Complex Test",
|
|
356
|
+
type: "object",
|
|
357
|
+
additionalProperties: false,
|
|
358
|
+
properties: {
|
|
359
|
+
validProp: { type: "string" },
|
|
360
|
+
propToBeKept: { type: "array", items: { type: "number" } },
|
|
361
|
+
objectWithMixedTypes: {
|
|
362
|
+
type: "object",
|
|
363
|
+
additionalProperties: true,
|
|
364
|
+
properties: {
|
|
365
|
+
subPropString: { type: "string" },
|
|
366
|
+
subPropNull: { type: "null" },
|
|
367
|
+
subPropArrayType: { type: ["integer", "null", "array", "string"] },
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
anotherArrayProp: { type: "array", items: { type: "boolean" } },
|
|
371
|
+
},
|
|
372
|
+
$defs: {
|
|
373
|
+
reusableInvalid: { type: "null" },
|
|
374
|
+
reusableValid: {
|
|
375
|
+
type: "object",
|
|
376
|
+
additionalProperties: { type: "string" },
|
|
377
|
+
properties: {
|
|
378
|
+
detail: { type: "string" },
|
|
379
|
+
unwantedList: { type: "array", items: { type: "string" } },
|
|
380
|
+
statusOptions: { type: ["number", "string", "null"] },
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
toBeEmptyDef: { type: "null" },
|
|
384
|
+
},
|
|
385
|
+
anyOf: [
|
|
386
|
+
{ type: "string" },
|
|
387
|
+
{ type: "array", items: { type: "object" } },
|
|
388
|
+
{ $ref: "#/$defs/reusableInvalid" },
|
|
389
|
+
{ type: ["boolean", "null", "integer"] },
|
|
390
|
+
],
|
|
391
|
+
},
|
|
392
|
+
expected: {
|
|
393
|
+
title: "Complex Test",
|
|
394
|
+
type: "object",
|
|
395
|
+
properties: {
|
|
396
|
+
validProp: { type: "string" },
|
|
397
|
+
propToBeKept: { type: "array", items: { type: "number" } },
|
|
398
|
+
objectWithMixedTypes: {
|
|
399
|
+
type: "object",
|
|
400
|
+
properties: {
|
|
401
|
+
subPropString: { type: "string" },
|
|
402
|
+
subPropArrayType: {
|
|
403
|
+
anyOf: [{ type: "integer" }, { type: "array" }, { type: "string" }],
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
anotherArrayProp: { type: "array", items: { type: "boolean" } },
|
|
408
|
+
},
|
|
409
|
+
$defs: {
|
|
410
|
+
reusableValid: {
|
|
411
|
+
type: "object",
|
|
412
|
+
properties: {
|
|
413
|
+
detail: { type: "string" },
|
|
414
|
+
unwantedList: { type: "array", items: { type: "string" } },
|
|
415
|
+
statusOptions: { anyOf: [{ type: "number" }, { type: "string" }] },
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
anyOf: [
|
|
420
|
+
{ type: "string" },
|
|
421
|
+
{ type: "array", items: { type: "object" } },
|
|
422
|
+
{ anyOf: [{ type: "boolean" }, { type: "integer" }] },
|
|
423
|
+
],
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
desc: "should remove properties if properties object becomes empty (all null)",
|
|
428
|
+
input: {
|
|
429
|
+
type: "object",
|
|
430
|
+
properties: {
|
|
431
|
+
field1: { type: "null" },
|
|
432
|
+
field2: { type: "null" },
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
expected: {
|
|
436
|
+
type: "object",
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
desc: "top level schema with type: ['string', 'array'] should become type: 'string'",
|
|
441
|
+
input: {
|
|
442
|
+
type: ["string", "array"],
|
|
443
|
+
description: "Test",
|
|
444
|
+
},
|
|
445
|
+
expected: {
|
|
446
|
+
type: "string",
|
|
447
|
+
description: "Test",
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
desc: "top level schema with type: ['string', 'number', 'array'] should become anyOf: [{type: string}, {type: number}]",
|
|
452
|
+
input: {
|
|
453
|
+
type: ["string", "number", "array"],
|
|
454
|
+
description: "Test AnyOf Root",
|
|
455
|
+
},
|
|
456
|
+
expected: {
|
|
457
|
+
anyOf: [{ type: "string" }, { type: "number" }],
|
|
458
|
+
description: "Test AnyOf Root",
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
];
|
|
462
|
+
describe("cleanSchema", () => {
|
|
463
|
+
testCases.forEach((tc) => {
|
|
464
|
+
it(tc.desc, () => {
|
|
465
|
+
(0, chai_1.expect)((0, util_1.cleanSchema)(tc.input)).to.deep.equal(tc.expected);
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
});
|
package/lib/prompt.js
CHANGED
|
@@ -70,7 +70,7 @@ async function number(opts) {
|
|
|
70
70
|
return value;
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
return
|
|
73
|
+
return await inquirer.number(Object.assign({ required: true }, opts));
|
|
74
74
|
}
|
|
75
75
|
exports.number = number;
|
|
76
76
|
async function password(opts) {
|
package/lib/track.js
CHANGED
|
@@ -25,7 +25,7 @@ exports.GA4_PROPERTIES = {
|
|
|
25
25
|
},
|
|
26
26
|
};
|
|
27
27
|
function usageEnabled() {
|
|
28
|
-
return !!process.env.IS_FIREBASE_CLI && !!configstore_1.configstore.get("usage");
|
|
28
|
+
return ((!!process.env.IS_FIREBASE_CLI || !!process.env.IS_FIREBASE_MCP) && !!configstore_1.configstore.get("usage"));
|
|
29
29
|
}
|
|
30
30
|
exports.usageEnabled = usageEnabled;
|
|
31
31
|
const GA4_USER_PROPS = {
|
package/package.json
CHANGED
|
@@ -90,6 +90,18 @@
|
|
|
90
90
|
],
|
|
91
91
|
"description": "Configuration for a generated Javascript SDK"
|
|
92
92
|
},
|
|
93
|
+
"dartSdk": {
|
|
94
|
+
"oneOf": [
|
|
95
|
+
{ "$ref": "#/definitions/dartSdk" },
|
|
96
|
+
{
|
|
97
|
+
"type": "array",
|
|
98
|
+
"items": {
|
|
99
|
+
"$ref": "#/definitions/dartSdk"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"description": "Configuration for a generated Dart SDK"
|
|
104
|
+
},
|
|
93
105
|
"kotlinSdk": {
|
|
94
106
|
"oneOf": [
|
|
95
107
|
{ "$ref": "#/definitions/kotlinSdk" },
|