eslint-plugin-firebase-ai-logic 1.2.0 → 1.4.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/dist/index.js +1 -1
- package/dist/rules/check-temperature-defaults.d.ts +24 -0
- package/dist/rules/check-temperature-defaults.d.ts.map +1 -1
- package/dist/rules/check-temperature-defaults.js +73 -7
- package/dist/rules/check-temperature-defaults.js.map +1 -1
- package/dist/rules/prefer-count-tokens.d.ts +24 -0
- package/dist/rules/prefer-count-tokens.d.ts.map +1 -1
- package/dist/rules/prefer-count-tokens.js +29 -3
- package/dist/rules/prefer-count-tokens.js.map +1 -1
- package/dist/rules/prefer-optional-properties.d.ts +18 -0
- package/dist/rules/prefer-optional-properties.d.ts.map +1 -1
- package/dist/rules/prefer-optional-properties.js +30 -2
- package/dist/rules/prefer-optional-properties.js.map +1 -1
- package/dist/rules/require-app-check-production.d.ts +21 -0
- package/dist/rules/require-app-check-production.d.ts.map +1 -1
- package/dist/rules/require-app-check-production.js +111 -13
- package/dist/rules/require-app-check-production.js.map +1 -1
- package/dist/rules/require-backend.d.ts +17 -0
- package/dist/rules/require-backend.d.ts.map +1 -1
- package/dist/rules/require-backend.js +35 -42
- package/dist/rules/require-backend.js.map +1 -1
- package/dist/rules/require-error-handling.d.ts +24 -0
- package/dist/rules/require-error-handling.d.ts.map +1 -1
- package/dist/rules/require-error-handling.js +96 -3
- package/dist/rules/require-error-handling.js.map +1 -1
- package/dist/rules/validate-schema-structure.d.ts +19 -0
- package/dist/rules/validate-schema-structure.d.ts.map +1 -1
- package/dist/rules/validate-schema-structure.js +58 -21
- package/dist/rules/validate-schema-structure.js.map +1 -1
- package/dist/utils/ast-helpers.d.ts +4 -0
- package/dist/utils/ast-helpers.d.ts.map +1 -1
- package/dist/utils/ast-helpers.js +21 -0
- package/dist/utils/ast-helpers.js.map +1 -1
- package/dist/utils/constants.d.ts +2 -2
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +12 -5
- package/dist/utils/constants.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import type { Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* check-temperature-defaults
|
|
4
|
+
*
|
|
5
|
+
* This rule suggests using the default temperature (1.0) for Gemini 3 models,
|
|
6
|
+
* but with important exceptions:
|
|
7
|
+
*
|
|
8
|
+
* ONLY APPLIES TO:
|
|
9
|
+
* - gemini-3-flash-preview
|
|
10
|
+
* - gemini-3-pro-preview
|
|
11
|
+
* (Other models like gemini-2.5-* don't have this recommendation)
|
|
12
|
+
*
|
|
13
|
+
* EXCEPTIONS (low temperature is valid and recommended):
|
|
14
|
+
* 1. Structured output (responseMimeType: 'application/json')
|
|
15
|
+
* - Low temperature (0.1-0.3) prevents hallucinations in extraction tasks
|
|
16
|
+
* 2. Classification tasks (maxOutputTokens <= 20)
|
|
17
|
+
* - Deterministic outputs benefit from low temperature
|
|
18
|
+
* 3. Data extraction with responseSchema
|
|
19
|
+
* - Precise extraction needs low temperature
|
|
20
|
+
*
|
|
21
|
+
* The rule only warns when:
|
|
22
|
+
* - Model is Gemini 3 (flash or pro preview)
|
|
23
|
+
* - Temperature is NOT 1.0
|
|
24
|
+
* - AND it's NOT a structured output or classification task
|
|
25
|
+
*/
|
|
2
26
|
declare const rule: Rule.RuleModule;
|
|
3
27
|
export default rule;
|
|
4
28
|
//# sourceMappingURL=check-temperature-defaults.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-temperature-defaults.d.ts","sourceRoot":"","sources":["../../src/rules/check-temperature-defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"check-temperature-defaults.d.ts","sourceRoot":"","sources":["../../src/rules/check-temperature-defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AASnC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UA4GhB,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -1,20 +1,51 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const ast_helpers_js_1 = require("../utils/ast-helpers.js");
|
|
4
|
+
/**
|
|
5
|
+
* check-temperature-defaults
|
|
6
|
+
*
|
|
7
|
+
* This rule suggests using the default temperature (1.0) for Gemini 3 models,
|
|
8
|
+
* but with important exceptions:
|
|
9
|
+
*
|
|
10
|
+
* ONLY APPLIES TO:
|
|
11
|
+
* - gemini-3-flash-preview
|
|
12
|
+
* - gemini-3-pro-preview
|
|
13
|
+
* (Other models like gemini-2.5-* don't have this recommendation)
|
|
14
|
+
*
|
|
15
|
+
* EXCEPTIONS (low temperature is valid and recommended):
|
|
16
|
+
* 1. Structured output (responseMimeType: 'application/json')
|
|
17
|
+
* - Low temperature (0.1-0.3) prevents hallucinations in extraction tasks
|
|
18
|
+
* 2. Classification tasks (maxOutputTokens <= 20)
|
|
19
|
+
* - Deterministic outputs benefit from low temperature
|
|
20
|
+
* 3. Data extraction with responseSchema
|
|
21
|
+
* - Precise extraction needs low temperature
|
|
22
|
+
*
|
|
23
|
+
* The rule only warns when:
|
|
24
|
+
* - Model is Gemini 3 (flash or pro preview)
|
|
25
|
+
* - Temperature is NOT 1.0
|
|
26
|
+
* - AND it's NOT a structured output or classification task
|
|
27
|
+
*/
|
|
4
28
|
const rule = {
|
|
5
29
|
meta: {
|
|
6
|
-
type: '
|
|
30
|
+
type: 'suggestion',
|
|
7
31
|
docs: {
|
|
8
|
-
description: '
|
|
32
|
+
description: 'Suggest default temperature (1.0) for Gemini 3 models, except for structured output and classification tasks',
|
|
9
33
|
recommended: true,
|
|
10
34
|
url: 'https://github.com/Just-mpm/eslint-plugin-firebase-ai-logic#check-temperature-defaults',
|
|
11
35
|
},
|
|
12
36
|
schema: [],
|
|
13
37
|
messages: {
|
|
14
|
-
nonDefaultTemperature: '
|
|
38
|
+
nonDefaultTemperature: 'Consider using the default temperature (1.0) for Gemini 3 models. Lower values may cause looping or degraded reasoning. Exception: low temperature is appropriate for structured output (JSON) and classification tasks.',
|
|
15
39
|
},
|
|
16
40
|
},
|
|
17
41
|
create(context) {
|
|
42
|
+
// Gemini 3 models that have the temperature=1.0 recommendation
|
|
43
|
+
const GEMINI_3_MODELS = [
|
|
44
|
+
'gemini-3-flash-preview',
|
|
45
|
+
'gemini-3-pro-preview',
|
|
46
|
+
'gemini-3-flash',
|
|
47
|
+
'gemini-3-pro',
|
|
48
|
+
];
|
|
18
49
|
return {
|
|
19
50
|
CallExpression(node) {
|
|
20
51
|
const calleeName = (0, ast_helpers_js_1.getCalleeName)(node);
|
|
@@ -27,20 +58,55 @@ const rule = {
|
|
|
27
58
|
const configArg = node.arguments[1];
|
|
28
59
|
if (!configArg || !(0, ast_helpers_js_1.isObjectExpression)(configArg))
|
|
29
60
|
return;
|
|
61
|
+
// Check model name - only apply to Gemini 3 models
|
|
62
|
+
const modelProp = (0, ast_helpers_js_1.findProperty)(configArg, 'model');
|
|
63
|
+
if (modelProp) {
|
|
64
|
+
const modelName = (0, ast_helpers_js_1.getStringValue)(modelProp.value);
|
|
65
|
+
if (modelName && !GEMINI_3_MODELS.some(m => modelName.includes(m))) {
|
|
66
|
+
// Not a Gemini 3 model - don't apply this rule
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
30
70
|
// Check for generationConfig
|
|
31
|
-
|
|
32
|
-
|
|
71
|
+
const generationConfig = (0, ast_helpers_js_1.findProperty)(configArg, 'generationConfig');
|
|
72
|
+
const configToCheck = generationConfig
|
|
33
73
|
? generationConfig.value
|
|
34
74
|
: configArg;
|
|
35
75
|
if (!(0, ast_helpers_js_1.isObjectExpression)(configToCheck))
|
|
36
76
|
return;
|
|
77
|
+
// Check for responseMimeType - if it's 'application/json', low temperature is valid
|
|
78
|
+
const responseMimeTypeProp = (0, ast_helpers_js_1.findProperty)(configToCheck, 'responseMimeType');
|
|
79
|
+
if (responseMimeTypeProp) {
|
|
80
|
+
const mimeType = (0, ast_helpers_js_1.getStringValue)(responseMimeTypeProp.value);
|
|
81
|
+
if (mimeType === 'application/json') {
|
|
82
|
+
// Structured output - low temperature is appropriate, don't warn
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Check for responseSchema - if present, it's structured output
|
|
87
|
+
const responseSchemaProp = (0, ast_helpers_js_1.findProperty)(configToCheck, 'responseSchema');
|
|
88
|
+
if (responseSchemaProp) {
|
|
89
|
+
// Has response schema - structured output, don't warn
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Check for maxOutputTokens - if very low (<= 20), it's likely classification
|
|
93
|
+
const maxOutputTokensProp = (0, ast_helpers_js_1.findProperty)(configToCheck, 'maxOutputTokens');
|
|
94
|
+
if (maxOutputTokensProp) {
|
|
95
|
+
const maxTokens = (0, ast_helpers_js_1.getNumberValue)(maxOutputTokensProp.value);
|
|
96
|
+
if (maxTokens !== null && maxTokens <= 20) {
|
|
97
|
+
// Classification task (very short output) - low temperature is appropriate
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
37
101
|
// Check for temperature property
|
|
38
102
|
const temperatureProp = (0, ast_helpers_js_1.findProperty)(configToCheck, 'temperature');
|
|
39
103
|
if (temperatureProp) {
|
|
40
|
-
// If value is literal and not 1.0,
|
|
104
|
+
// If value is literal and not 1.0, suggest (not require) using default
|
|
41
105
|
if (temperatureProp.value.type === 'Literal' &&
|
|
42
106
|
typeof temperatureProp.value.value === 'number') {
|
|
43
|
-
|
|
107
|
+
const temp = temperatureProp.value.value;
|
|
108
|
+
// Only warn for non-default temperature when NOT doing structured output
|
|
109
|
+
if (temp !== 1.0) {
|
|
44
110
|
context.report({
|
|
45
111
|
node: temperatureProp,
|
|
46
112
|
messageId: 'nonDefaultTemperature',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-temperature-defaults.js","sourceRoot":"","sources":["../../src/rules/check-temperature-defaults.ts"],"names":[],"mappings":";;AACA,
|
|
1
|
+
{"version":3,"file":"check-temperature-defaults.js","sourceRoot":"","sources":["../../src/rules/check-temperature-defaults.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import type { Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* prefer-count-tokens
|
|
4
|
+
*
|
|
5
|
+
* This rule suggests using countTokens() before large requests.
|
|
6
|
+
*
|
|
7
|
+
* WHY 2000 CHARACTERS DEFAULT?
|
|
8
|
+
* - 2000 chars ≈ 500 tokens (roughly 4 chars per token)
|
|
9
|
+
* - Cost increases significantly above 200K tokens context
|
|
10
|
+
* - countTokens API is FREE and has 3000 RPM quota
|
|
11
|
+
* - Below 500 tokens, the cost impact is minimal (~$0.0001 for flash)
|
|
12
|
+
*
|
|
13
|
+
* Based on Google's pricing:
|
|
14
|
+
* - Gemini 2.5 Flash-Lite: $0.10/M input tokens
|
|
15
|
+
* - Gemini 2.5 Flash: $0.15/M input tokens
|
|
16
|
+
* - Gemini 3 Pro: $2.00/M input tokens (up to 200K), $4.00/M above
|
|
17
|
+
*
|
|
18
|
+
* The rule is meant to catch large prompts that could:
|
|
19
|
+
* 1. Unexpectedly increase costs
|
|
20
|
+
* 2. Risk exceeding context window limits
|
|
21
|
+
* 3. Benefit from cost estimation before sending
|
|
22
|
+
*
|
|
23
|
+
* @see https://ai.google.dev/gemini-api/docs/tokens
|
|
24
|
+
* @see https://ai.google.dev/gemini-api/docs/pricing
|
|
25
|
+
*/
|
|
2
26
|
declare const rule: Rule.RuleModule;
|
|
3
27
|
export default rule;
|
|
4
28
|
//# sourceMappingURL=prefer-count-tokens.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-count-tokens.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-count-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAGnC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"prefer-count-tokens.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-count-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAGnC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UA4HhB,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const ast_helpers_js_1 = require("../utils/ast-helpers.js");
|
|
4
|
+
/**
|
|
5
|
+
* prefer-count-tokens
|
|
6
|
+
*
|
|
7
|
+
* This rule suggests using countTokens() before large requests.
|
|
8
|
+
*
|
|
9
|
+
* WHY 2000 CHARACTERS DEFAULT?
|
|
10
|
+
* - 2000 chars ≈ 500 tokens (roughly 4 chars per token)
|
|
11
|
+
* - Cost increases significantly above 200K tokens context
|
|
12
|
+
* - countTokens API is FREE and has 3000 RPM quota
|
|
13
|
+
* - Below 500 tokens, the cost impact is minimal (~$0.0001 for flash)
|
|
14
|
+
*
|
|
15
|
+
* Based on Google's pricing:
|
|
16
|
+
* - Gemini 2.5 Flash-Lite: $0.10/M input tokens
|
|
17
|
+
* - Gemini 2.5 Flash: $0.15/M input tokens
|
|
18
|
+
* - Gemini 3 Pro: $2.00/M input tokens (up to 200K), $4.00/M above
|
|
19
|
+
*
|
|
20
|
+
* The rule is meant to catch large prompts that could:
|
|
21
|
+
* 1. Unexpectedly increase costs
|
|
22
|
+
* 2. Risk exceeding context window limits
|
|
23
|
+
* 3. Benefit from cost estimation before sending
|
|
24
|
+
*
|
|
25
|
+
* @see https://ai.google.dev/gemini-api/docs/tokens
|
|
26
|
+
* @see https://ai.google.dev/gemini-api/docs/pricing
|
|
27
|
+
*/
|
|
4
28
|
const rule = {
|
|
5
29
|
meta: {
|
|
6
30
|
type: 'suggestion',
|
|
@@ -15,8 +39,8 @@ const rule = {
|
|
|
15
39
|
properties: {
|
|
16
40
|
promptLengthThreshold: {
|
|
17
41
|
type: 'number',
|
|
18
|
-
default:
|
|
19
|
-
description: 'Character length threshold to suggest token counting',
|
|
42
|
+
default: 2000,
|
|
43
|
+
description: 'Character length threshold to suggest token counting (default: 2000 chars ≈ 500 tokens)',
|
|
20
44
|
},
|
|
21
45
|
},
|
|
22
46
|
additionalProperties: false,
|
|
@@ -29,7 +53,9 @@ const rule = {
|
|
|
29
53
|
},
|
|
30
54
|
create(context) {
|
|
31
55
|
const options = context.options[0] ?? {};
|
|
32
|
-
|
|
56
|
+
// Default threshold: 2000 chars ≈ 500 tokens
|
|
57
|
+
// This is a reasonable threshold where token counting becomes useful
|
|
58
|
+
const promptLengthThreshold = options.promptLengthThreshold ?? 2000;
|
|
33
59
|
// Track if countTokens was called in the same scope
|
|
34
60
|
const scopesWithCountTokens = new Set();
|
|
35
61
|
function getScopeKey(node) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-count-tokens.js","sourceRoot":"","sources":["../../src/rules/prefer-count-tokens.ts"],"names":[],"mappings":";;AACA,4DAAwE;AAExE,MAAM,IAAI,GAAoB;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACJ,WAAW,EACT,+FAA+F;YACjG,WAAW,EAAE,KAAK;YAClB,GAAG,EAAE,iFAAiF;SACvF;QACD,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,qBAAqB,EAAE;wBACrB,IAAI,EAAE,QAAQ;wBACd,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"prefer-count-tokens.js","sourceRoot":"","sources":["../../src/rules/prefer-count-tokens.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import type { Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* prefer-optional-properties
|
|
4
|
+
*
|
|
5
|
+
* This rule suggests using 'optionalProperties' array for optional fields,
|
|
6
|
+
* but with important exceptions:
|
|
7
|
+
*
|
|
8
|
+
* EXCEPTIONS (no warning):
|
|
9
|
+
* 1. If the schema has a 'required' array defined
|
|
10
|
+
* - This is the standard way to define required fields in function calling
|
|
11
|
+
* - Fields not in 'required' are implicitly optional
|
|
12
|
+
* 2. If the schema is for function calling parameters
|
|
13
|
+
* - Function calling uses 'required' array pattern
|
|
14
|
+
*
|
|
15
|
+
* The rule only warns when:
|
|
16
|
+
* - Schema has many properties (>5)
|
|
17
|
+
* - AND no 'optionalProperties' array
|
|
18
|
+
* - AND no 'required' array (which would indicate intent)
|
|
19
|
+
*/
|
|
2
20
|
declare const rule: Rule.RuleModule;
|
|
3
21
|
export default rule;
|
|
4
22
|
//# sourceMappingURL=prefer-optional-properties.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-optional-properties.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-optional-properties.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAQnC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"prefer-optional-properties.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-optional-properties.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAQnC;;;;;;;;;;;;;;;;;GAiBG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAqHhB,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const ast_helpers_js_1 = require("../utils/ast-helpers.js");
|
|
4
|
+
/**
|
|
5
|
+
* prefer-optional-properties
|
|
6
|
+
*
|
|
7
|
+
* This rule suggests using 'optionalProperties' array for optional fields,
|
|
8
|
+
* but with important exceptions:
|
|
9
|
+
*
|
|
10
|
+
* EXCEPTIONS (no warning):
|
|
11
|
+
* 1. If the schema has a 'required' array defined
|
|
12
|
+
* - This is the standard way to define required fields in function calling
|
|
13
|
+
* - Fields not in 'required' are implicitly optional
|
|
14
|
+
* 2. If the schema is for function calling parameters
|
|
15
|
+
* - Function calling uses 'required' array pattern
|
|
16
|
+
*
|
|
17
|
+
* The rule only warns when:
|
|
18
|
+
* - Schema has many properties (>5)
|
|
19
|
+
* - AND no 'optionalProperties' array
|
|
20
|
+
* - AND no 'required' array (which would indicate intent)
|
|
21
|
+
*/
|
|
4
22
|
const rule = {
|
|
5
23
|
meta: {
|
|
6
24
|
type: 'suggestion',
|
|
@@ -13,7 +31,7 @@ const rule = {
|
|
|
13
31
|
messages: {
|
|
14
32
|
useOptionalProperties: "In Firebase AI Logic, all schema fields are required by default. Use 'optionalProperties: [\"fieldName\"]' array to mark fields as optional.",
|
|
15
33
|
nullableNotOptional: "Using 'nullable: true' makes the field accept null but still requires it. Use 'optionalProperties' array to make it truly optional.",
|
|
16
|
-
missingOptionalProperties: "Schema has many properties but no 'optionalProperties' array. Consider which fields should be optional.",
|
|
34
|
+
missingOptionalProperties: "Schema has many properties but no 'optionalProperties' or 'required' array. Consider which fields should be optional.",
|
|
17
35
|
},
|
|
18
36
|
},
|
|
19
37
|
create(context) {
|
|
@@ -22,6 +40,12 @@ const rule = {
|
|
|
22
40
|
return;
|
|
23
41
|
const propertiesProp = (0, ast_helpers_js_1.findProperty)(obj, 'properties');
|
|
24
42
|
const optionalPropertiesProp = (0, ast_helpers_js_1.findProperty)(obj, 'optionalProperties');
|
|
43
|
+
const requiredProp = (0, ast_helpers_js_1.findProperty)(obj, 'required');
|
|
44
|
+
// If 'required' array is defined, the developer has explicitly chosen
|
|
45
|
+
// which fields are required - no need to warn about optionalProperties
|
|
46
|
+
if (requiredProp) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
25
49
|
// Check for nullable fields without optionalProperties
|
|
26
50
|
if (propertiesProp && (0, ast_helpers_js_1.isObjectExpression)(propertiesProp.value)) {
|
|
27
51
|
const propsObj = propertiesProp.value;
|
|
@@ -55,7 +79,11 @@ const rule = {
|
|
|
55
79
|
// Check if schema has many properties but no optionalProperties
|
|
56
80
|
if (propertiesProp && (0, ast_helpers_js_1.isObjectExpression)(propertiesProp.value)) {
|
|
57
81
|
const propertyCount = propertiesProp.value.properties.filter(ast_helpers_js_1.isProperty).length;
|
|
58
|
-
|
|
82
|
+
// Only warn if:
|
|
83
|
+
// - Many properties (>5)
|
|
84
|
+
// - No optionalProperties
|
|
85
|
+
// - No required array (which indicates intentional field selection)
|
|
86
|
+
if (propertyCount > 5 && !optionalPropertiesProp && !requiredProp) {
|
|
59
87
|
context.report({
|
|
60
88
|
node: obj,
|
|
61
89
|
messageId: 'missingOptionalProperties',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-optional-properties.js","sourceRoot":"","sources":["../../src/rules/prefer-optional-properties.ts"],"names":[],"mappings":";;AACA,4DAKiC;AAEjC,MAAM,IAAI,GAAoB;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACJ,WAAW,EACT,oGAAoG;YACtG,WAAW,EAAE,IAAI;YACjB,GAAG,EAAE,wFAAwF;SAC9F;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,qBAAqB,EACnB,8IAA8I;YAChJ,mBAAmB,EACjB,qIAAqI;YACvI,yBAAyB,EACvB,
|
|
1
|
+
{"version":3,"file":"prefer-optional-properties.js","sourceRoot":"","sources":["../../src/rules/prefer-optional-properties.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import type { Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* require-app-check-production
|
|
4
|
+
*
|
|
5
|
+
* This rule suggests using App Check to protect AI endpoints from abuse.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT LIMITATIONS:
|
|
8
|
+
* - This rule only checks within the SAME FILE
|
|
9
|
+
* - Many projects use lazy loading where App Check is initialized in a separate file
|
|
10
|
+
* - Projects using hooks like useAppCheck() or ensureAppCheckReady() are valid
|
|
11
|
+
*
|
|
12
|
+
* The rule checks for:
|
|
13
|
+
* 1. Direct import of 'firebase/app-check'
|
|
14
|
+
* 2. Calls to initializeAppCheck()
|
|
15
|
+
* 3. Common patterns: useAppCheck, ensureAppCheckReady, AppCheckProvider
|
|
16
|
+
* 4. Module-level detection: If the file imports from a module that handles AI
|
|
17
|
+
* (like './ai' or '../firebase/ai'), it's likely App Check is handled there
|
|
18
|
+
*
|
|
19
|
+
* If none are found in the same file as getAI(), it suggests (not requires) App Check.
|
|
20
|
+
*
|
|
21
|
+
* CONFIGURABLE: Set 'ignoreFiles' to skip specific files/patterns.
|
|
22
|
+
*/
|
|
2
23
|
declare const rule: Rule.RuleModule;
|
|
3
24
|
export default rule;
|
|
4
25
|
//# sourceMappingURL=require-app-check-production.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"require-app-check-production.d.ts","sourceRoot":"","sources":["../../src/rules/require-app-check-production.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAGnC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"require-app-check-production.d.ts","sourceRoot":"","sources":["../../src/rules/require-app-check-production.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAGnC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UA0JhB,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const ast_helpers_js_1 = require("../utils/ast-helpers.js");
|
|
4
|
+
/**
|
|
5
|
+
* require-app-check-production
|
|
6
|
+
*
|
|
7
|
+
* This rule suggests using App Check to protect AI endpoints from abuse.
|
|
8
|
+
*
|
|
9
|
+
* IMPORTANT LIMITATIONS:
|
|
10
|
+
* - This rule only checks within the SAME FILE
|
|
11
|
+
* - Many projects use lazy loading where App Check is initialized in a separate file
|
|
12
|
+
* - Projects using hooks like useAppCheck() or ensureAppCheckReady() are valid
|
|
13
|
+
*
|
|
14
|
+
* The rule checks for:
|
|
15
|
+
* 1. Direct import of 'firebase/app-check'
|
|
16
|
+
* 2. Calls to initializeAppCheck()
|
|
17
|
+
* 3. Common patterns: useAppCheck, ensureAppCheckReady, AppCheckProvider
|
|
18
|
+
* 4. Module-level detection: If the file imports from a module that handles AI
|
|
19
|
+
* (like './ai' or '../firebase/ai'), it's likely App Check is handled there
|
|
20
|
+
*
|
|
21
|
+
* If none are found in the same file as getAI(), it suggests (not requires) App Check.
|
|
22
|
+
*
|
|
23
|
+
* CONFIGURABLE: Set 'ignoreFiles' to skip specific files/patterns.
|
|
24
|
+
*/
|
|
4
25
|
const rule = {
|
|
5
26
|
meta: {
|
|
6
27
|
type: 'suggestion',
|
|
@@ -9,43 +30,120 @@ const rule = {
|
|
|
9
30
|
recommended: true,
|
|
10
31
|
url: 'https://github.com/Just-mpm/eslint-plugin-firebase-ai-logic#require-app-check-production',
|
|
11
32
|
},
|
|
12
|
-
schema: [
|
|
33
|
+
schema: [
|
|
34
|
+
{
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
ignoreFiles: {
|
|
38
|
+
type: 'array',
|
|
39
|
+
items: { type: 'string' },
|
|
40
|
+
default: [],
|
|
41
|
+
description: 'File patterns to ignore (e.g., "*.test.ts", "test/**/*")',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
additionalProperties: false,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
13
47
|
messages: {
|
|
14
|
-
missingAppCheck: 'Consider using App Check
|
|
15
|
-
appCheckSuggestion: "import { initializeAppCheck,
|
|
48
|
+
missingAppCheck: 'Consider using App Check to protect your AI endpoints from abuse. If you use lazy loading (useAppCheck, ensureAppCheckReady) in a separate file, you can safely ignore this warning.',
|
|
49
|
+
appCheckSuggestion: "import { initializeAppCheck, ReCaptchaEnterpriseProvider } from 'firebase/app-check'; initializeAppCheck(app, { provider: new ReCaptchaEnterpriseProvider('SITE_KEY') });",
|
|
16
50
|
},
|
|
17
51
|
},
|
|
18
52
|
create(context) {
|
|
19
53
|
let hasAppCheckImport = false;
|
|
20
54
|
let hasAppCheckInit = false;
|
|
55
|
+
let hasAppCheckHelper = false;
|
|
21
56
|
let hasAIInit = false;
|
|
22
57
|
let aiInitNode = null;
|
|
58
|
+
let importsFromAIModule = false;
|
|
59
|
+
// Common App Check helper patterns used in lazy loading
|
|
60
|
+
const appCheckHelperPatterns = [
|
|
61
|
+
'useAppCheck',
|
|
62
|
+
'ensureAppCheckReady',
|
|
63
|
+
'AppCheckProvider',
|
|
64
|
+
'getAppCheckInstance',
|
|
65
|
+
'isAppCheckReady',
|
|
66
|
+
'initializeAppCheckLazy',
|
|
67
|
+
'withAppCheck',
|
|
68
|
+
'appCheckReady',
|
|
69
|
+
];
|
|
70
|
+
// Common AI module patterns where App Check might be configured
|
|
71
|
+
const aiModulePatterns = [
|
|
72
|
+
'/ai',
|
|
73
|
+
'/firebase/ai',
|
|
74
|
+
'/lib/ai',
|
|
75
|
+
'/services/ai',
|
|
76
|
+
'getAIInstance',
|
|
77
|
+
'aiService',
|
|
78
|
+
];
|
|
23
79
|
return {
|
|
24
80
|
// Track App Check imports
|
|
25
81
|
ImportDeclaration(node) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
82
|
+
const source = node.source.value;
|
|
83
|
+
if (typeof source !== 'string')
|
|
84
|
+
return;
|
|
85
|
+
// Direct firebase/app-check import
|
|
86
|
+
if (source === 'firebase/app-check') {
|
|
30
87
|
hasAppCheckImport = true;
|
|
31
88
|
}
|
|
89
|
+
// Check for imports from common App Check helper files/hooks
|
|
90
|
+
if (source.includes('useAppCheck') ||
|
|
91
|
+
source.includes('AppCheck') ||
|
|
92
|
+
source.includes('appCheck')) {
|
|
93
|
+
hasAppCheckHelper = true;
|
|
94
|
+
}
|
|
95
|
+
// Check if importing from a common AI module pattern
|
|
96
|
+
// These modules typically handle App Check internally
|
|
97
|
+
if (aiModulePatterns.some((pattern) => source.includes(pattern))) {
|
|
98
|
+
importsFromAIModule = true;
|
|
99
|
+
}
|
|
100
|
+
// Check imported specifiers for App Check helpers
|
|
101
|
+
for (const spec of node.specifiers) {
|
|
102
|
+
if (spec.type === 'ImportSpecifier' && spec.imported.type === 'Identifier') {
|
|
103
|
+
const importedName = spec.imported.name;
|
|
104
|
+
if (appCheckHelperPatterns.includes(importedName)) {
|
|
105
|
+
hasAppCheckHelper = true;
|
|
106
|
+
}
|
|
107
|
+
// Check if importing getAIInstance or similar from another module
|
|
108
|
+
if (importedName === 'getAIInstance' ||
|
|
109
|
+
importedName === 'getAI' ||
|
|
110
|
+
importedName === 'aiInstance') {
|
|
111
|
+
importsFromAIModule = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
32
115
|
},
|
|
33
|
-
// Track
|
|
116
|
+
// Track function calls
|
|
34
117
|
CallExpression(node) {
|
|
35
118
|
const calleeName = (0, ast_helpers_js_1.getCalleeName)(node);
|
|
119
|
+
if (!calleeName)
|
|
120
|
+
return;
|
|
121
|
+
// Direct initializeAppCheck call
|
|
36
122
|
if (calleeName === 'initializeAppCheck') {
|
|
37
123
|
hasAppCheckInit = true;
|
|
38
124
|
}
|
|
39
|
-
//
|
|
125
|
+
// App Check helper calls
|
|
126
|
+
if (appCheckHelperPatterns.includes(calleeName)) {
|
|
127
|
+
hasAppCheckHelper = true;
|
|
128
|
+
}
|
|
129
|
+
// Track getAI calls - but only if it's a DIRECT call, not imported wrapper
|
|
40
130
|
if (calleeName === 'getAI') {
|
|
41
|
-
|
|
42
|
-
|
|
131
|
+
// Check if this is the actual firebase/ai getAI or a wrapper
|
|
132
|
+
// If we already import from an AI module, the App Check is likely handled there
|
|
133
|
+
if (!importsFromAIModule) {
|
|
134
|
+
hasAIInit = true;
|
|
135
|
+
aiInitNode = node;
|
|
136
|
+
}
|
|
43
137
|
}
|
|
44
138
|
},
|
|
45
139
|
// Check at the end of the program
|
|
46
140
|
'Program:exit'() {
|
|
47
|
-
// Only
|
|
48
|
-
|
|
141
|
+
// Only suggest if:
|
|
142
|
+
// 1. AI is being used directly (not via imported wrapper)
|
|
143
|
+
// 2. No App Check patterns found
|
|
144
|
+
// 3. Not importing from a module that likely handles App Check
|
|
145
|
+
const hasAnyAppCheckPattern = hasAppCheckImport || hasAppCheckInit || hasAppCheckHelper || importsFromAIModule;
|
|
146
|
+
if (hasAIInit && !hasAnyAppCheckPattern && aiInitNode) {
|
|
49
147
|
context.report({
|
|
50
148
|
node: aiInitNode,
|
|
51
149
|
messageId: 'missingAppCheck',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"require-app-check-production.js","sourceRoot":"","sources":["../../src/rules/require-app-check-production.ts"],"names":[],"mappings":";;AACA,4DAAwD;AAExD,MAAM,IAAI,GAAoB;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACJ,WAAW,EACT,iFAAiF;YACnF,WAAW,EAAE,IAAI;YACjB,GAAG,EAAE,0FAA0F;SAChG;QACD,MAAM,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"require-app-check-production.js","sourceRoot":"","sources":["../../src/rules/require-app-check-production.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import type { Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* require-backend
|
|
4
|
+
*
|
|
5
|
+
* IMPORTANT: getAI(app) without a second argument is VALID.
|
|
6
|
+
* Firebase AI Logic uses GoogleAIBackend by default when no backend is specified.
|
|
7
|
+
*
|
|
8
|
+
* This rule only reports when:
|
|
9
|
+
* - A second argument (config object) exists BUT doesn't have 'backend' property
|
|
10
|
+
*
|
|
11
|
+
* Valid usage:
|
|
12
|
+
* - getAI(app) - Uses GoogleAIBackend by default ✅
|
|
13
|
+
* - getAI(app, { backend: new GoogleAIBackend() }) ✅
|
|
14
|
+
* - getAI(app, { backend: new VertexAIBackend() }) ✅
|
|
15
|
+
*
|
|
16
|
+
* Invalid usage:
|
|
17
|
+
* - getAI(app, { someOtherConfig: true }) - Has config but no backend ❌
|
|
18
|
+
*/
|
|
2
19
|
declare const rule: Rule.RuleModule;
|
|
3
20
|
export default rule;
|
|
4
21
|
//# sourceMappingURL=require-backend.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"require-backend.d.ts","sourceRoot":"","sources":["../../src/rules/require-backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAOnC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"require-backend.d.ts","sourceRoot":"","sources":["../../src/rules/require-backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAOnC;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAyEhB,CAAC;AAEF,eAAe,IAAI,CAAC"}
|