Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ npm run build
python3 scripts/deploy_ftp.py
```

If no deploy script exists, upload every file under `dist/` to the FTP account root, preserving subdirectories.
`scripts/deploy_ftp.py` mirrors `dist/` onto the FTP account root: it uploads
every local file, then prunes any remote file that no longer exists locally,
scoped to the directories `dist/` itself manages (the root, `api/`,
`assets/`). This exists because Vite content-hashes built assets
(`index-<hash>.js`), so a stale bundle from the previous build would otherwise
sit on the server forever. It never lists or deletes a remote directory that
has no local counterpart. Use `--dry-run` to preview without changing the
server.

## FlutterFlow custom-class deploys

Expand Down
54 changes: 49 additions & 5 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import {
createBuildShipContext,
} from "./src/pipelineContracts.js";
import { createModelArmorError } from "./src/modelArmorResponse.js";
import { validateBundleCompatibility } from "./src/flutterFlowArtifactValidation.js";
import {
getCustomActionReturnTypeError,
getDeclaredDartTypes,
validateBundleCompatibility,
} from "./src/flutterFlowArtifactValidation.js";
import { buildBundleDeployPlan } from "./src/bundleDeployPlanner.js";
import {
excludeProvisionedCodeFiles,
Expand Down Expand Up @@ -308,6 +312,11 @@ const FF_ARTIFACT_TYPES = `## THE FOUR ARTIFACT SURFACES
### B) Custom Actions (Async/Side Effects Silo)
- **Purpose:** API calls, complex logic, third-party libraries
- **Return type:** ALWAYS Future<T>
- **Supported Return Values:** Use only types exposed by FlutterFlow's Custom Action Return Value selector.
- **Structured results:** Default to JSON with \`Future<dynamic>\` or \`Future<Map<String, dynamic>>\`, and return a JSON-compatible Map/List (for example, \`result.toJson()\`).
- **FlutterFlow Data Types:** \`Future<SomeNameStruct>\` is allowed only when that Data Type already exists in the project.
- **Forbidden:** Never return an arbitrary CustomClass, Code File class, or CustomEnum from a Custom Action. FlutterFlow supports those in state/parameters, but not as Custom Action return values.
- **State workaround:** Write to App/Page State and return \`bool\` only when the user explicitly requests it and the required state variable is documented. Never assume state exists.
- **Imports:**
- External packages: include (e.g., \`import 'package:flutter_tts/flutter_tts.dart';\`)
- FlutterFlow imports: DO NOT include - added at commit
Expand Down Expand Up @@ -382,7 +391,7 @@ Page State variables (local to a single page) support everything App State does,
- When the user needs to store byte data: use a callback to pass bytes back to FlutterFlow (user can store in Page State), or convert to base64 String for App State storage, or upload to storage and store the resulting URL as ImagePath.
- NEVER generate code that writes FFUploadedFile or Uint8List to FFAppState — it will not compile.

**IMPORTANT:** Custom Dart classes for data exchange are now allowed via "Code Files", but Structs are still preferred for parameters visible in the UI builder.`;
**IMPORTANT:** Custom Dart classes are allowed through Code Files for state and supported parameters. They are NOT valid Custom Action return values; use JSON or an existing FlutterFlow Struct instead.`;

const FF_STATE_PATTERNS = `## STATE & DATA: FFAppState Patterns

Expand Down Expand Up @@ -444,6 +453,7 @@ const FF_FORBIDDEN_PATTERNS = `## FORBIDDEN PATTERNS (Will cause build failures)
- Adding custom imports to Custom Functions (strictly forbidden).
- Using complex parameter types (EdgeInsets, Duration, TextStyle) in Widgets/Actions.
- Using generics or function-typed fields in Code Files.
- Returning a CustomClass, Code File class, or CustomEnum from a Custom Action. Use JSON or an existing FlutterFlow Data Type (\`*Struct\`).

### ⛔ CRITICAL: RESERVED PARAMETER NAMES (INSTANT COMPILATION FAILURE)

Expand Down Expand Up @@ -1821,7 +1831,15 @@ class FlutterFlowApiClient {
console.log(
`Push attempt ${attempt + 1} to ${baseUrl}syncCustomCodeChanges`,
);
console.log("Request:", JSON.stringify(pushCodeRequest, null, 2));
console.log("Request metadata:", {
project_id: pushCodeRequest.project_id,
branch_name: pushCodeRequest.branch_name,
uid: pushCodeRequest.uid,
zipped_custom_code_length:
pushCodeRequest.zipped_custom_code?.length || 0,
file_map_length: pushCodeRequest.file_map?.length || 0,
functions_map_length: pushCodeRequest.functions_map?.length || 0,
});
const response = await fetch(`${baseUrl}syncCustomCodeChanges`, {
method: "POST",
headers: {
Expand Down Expand Up @@ -2588,7 +2606,13 @@ function buildCommitMetadata(codeInfo, pipelineResult = {}) {
* @param {string} content - File content
* @returns {Object} Validation result { valid: boolean, errors: string[] }
*/
function validateDartFile(fileName, content, codeType) {
function validateDartFile(
fileName,
content,
codeType,
declaredTypes = new Set(),
artifactName = "",
) {
const errors = [];
const hasWidgetClass = WIDGET_CLASS_REGEX.test(content);
const hasStateClass = STATE_CLASS_REGEX.test(content);
Expand Down Expand Up @@ -2660,6 +2684,14 @@ function validateDartFile(fileName, content, codeType) {
);
}

if (codeType === CodeType.ACTION) {
const returnTypeError = getCustomActionReturnTypeError(content, {
functionName: artifactName,
declaredTypes,
});
if (returnTypeError) errors.push(returnTypeError);
}

return {
valid: errors.length === 0,
errors,
Expand All @@ -2680,6 +2712,12 @@ function validateFileMap(fileMap) {
return { valid: false, errors, warnings };
}

const declaredTypes = new Set(
Array.from(fileMap.values()).flatMap((fileInfo) =>
getDeclaredDartTypes(fileInfo.content || "")
),
);

for (const [path, fileInfo] of fileMap.entries()) {
// Check for empty files
if (!fileInfo.content || fileInfo.content.trim().length === 0) {
Expand All @@ -2693,7 +2731,13 @@ function validateFileMap(fileMap) {

// Validate Dart files
if (path.endsWith(".dart")) {
const result = validateDartFile(path, fileInfo.content, fileInfo.type);
const result = validateDartFile(
path,
fileInfo.content,
fileInfo.type,
declaredTypes,
fileInfo.artifactName,
);
if (!result.valid) {
errors.push(...result.errors.map((e) => `${path}: ${e}`));
}
Expand Down
188 changes: 0 additions & 188 deletions dist/assets/index-B5FTuKa7.js

This file was deleted.

305 changes: 305 additions & 0 deletions dist/assets/index-CjrGBKbn.js

Large diffs are not rendered by default.

Loading
Loading