Skip to content
Open
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
219 changes: 47 additions & 172 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,173 +1,48 @@
# App Hosting adapters
import React, { Ethiopia } from 'react';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The import from 'react' is incorrect. useState is used in the component (line 8) but is not imported. Additionally, Ethiopia is not a valid export from the 'react' library. You should import useState instead.

Suggested change
import React, { Ethiopia } from 'react';
import React, { useState } from 'react';


// 1. የባለቤትነት መረጃ እና ግራፊክስ ስታይል
const OWNER_NAME = "betselot tamiru";
const AI_NAME = "ethiopia ai";

const App = () => {
const [messages, setMessages] = useState([{
role: 'bot',
content: `ሰላም! እኔ ${Ethiopia ai} እባላለሁ። የተፈጠርኩት በ ${betselot tamiru} ነው። እንዴት ልረዳህ እችላለሁ?`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The template literal is using invalid variable names (Ethiopia ai, betselot tamiru). You should use the constants AI_NAME and OWNER_NAME defined above. Template literal placeholders must contain valid JavaScript expressions, such as variable names.

Suggested change
content: `ሰላም! እኔ ${Ethiopia ai} እባላለሁ። የተፈጠርኩት በ ${betselot tamiru} ነው። እንዴት ልረዳህ እችላለሁ?`
content: `ሰላም! እኔ ${AI_NAME} እባላለሁ። የተፈጠርኩት በ ${OWNER_NAME} ነው። እንዴት ልረዳህ እችላለሁ?`

}]);

// ለግራፊክስ የተሰሩ ስታይሎች
const styles = {
body: { backgroundColor: '#0f172a', height: '100vh', display: 'flex', flexDirection: 'column' as 'column', color: 'white' },
header: { padding: '20px', textAlign: 'center' as 'center', background: 'linear-gradient(90deg, #4f46e5, #9333ea)', fontWeight: 'bold' },
chatBox: { flex: 1, overflowY: 'auto' as 'auto', padding: '20px' },
Comment on lines +15 to +17

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The as 'column', as 'center', and as 'auto' syntax is for TypeScript type assertions. This file appears to be a JavaScript file, where this syntax is invalid and will cause a parsing error. If you intend to use TypeScript, the file should have a .tsx extension. If it's JavaScript, you should remove these type assertions. The style properties will still work correctly without them.

Suggested change
body: { backgroundColor: '#0f172a', height: '100vh', display: 'flex', flexDirection: 'column' as 'column', color: 'white' },
header: { padding: '20px', textAlign: 'center' as 'center', background: 'linear-gradient(90deg, #4f46e5, #9333ea)', fontWeight: 'bold' },
chatBox: { flex: 1, overflowY: 'auto' as 'auto', padding: '20px' },
body: { backgroundColor: '#0f172a', height: '100vh', display: 'flex', flexDirection: 'column', color: 'white' },
header: { padding: '20px', textAlign: 'center', background: 'linear-gradient(90deg, #4f46e5, #9333ea)', fontWeight: 'bold' },
chatBox: { flex: 1, overflowY: 'auto', padding: '20px' },

inputArea: { padding: '20px', display: 'flex', gap: '10px', backgroundColor: '#1e293b' },
input: { flex: 1, padding: '12px', borderRadius: '8px', border: 'none', outline: 'none' },
button: { padding: '12px 24px', backgroundColor: '#4f46e5', border: 'none', borderRadius: '8px', color: 'white', cursor: 'pointer' }
};

return (
<div style={styles.body}>
<div style={styles.header}>
{AI_NAME} - በ {OWNER_NAME} የተመራ
</div>

<div style={styles.chatBox}>
{messages.map((m, i) => (
<div key={i} style={{ marginBottom: '15px', textAlign: m.role === 'user' ? 'right' : 'left' }}>
<span style={{ padding: '10px', borderRadius: '10px', backgroundColor: m.role === 'user' ? '#4f46e5' : '#334155', display: 'inline-block' }}>
{m.content}
</span>
</div>
))}
Comment on lines +30 to +36

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There are a couple of opportunities to improve this section according to React best practices:

  1. Unique Keys: Using the array index i as a key is an anti-pattern. It can cause rendering issues when the list changes. It's better to use a stable, unique ID from each message object (e.g., key={message.id}). You would need to add an id property to your message objects in the state.

  2. Inline Styles in Loops: Creating new style objects on every render (for both the div and span) is inefficient. These styles should be defined once in the styles object and referenced inside the loop. You can create variants for user and bot messages.

Here's an example of how you could refactor this:

// In your styles object, add:
messageWrapper: { marginBottom: '15px' },
botMessage: { padding: '10px', borderRadius: '10px', backgroundColor: '#334155', display: 'inline-block' },
userMessage: { padding: '10px', borderRadius: '10px', backgroundColor: '#4f46e5', display: 'inline-block' },

// In your component's return:
{messages.map((m) => (
  <div key={m.id} style={{ ...styles.messageWrapper, textAlign: m.role === 'user' ? 'right' : 'left' }}>
    <span style={m.role === 'user' ? styles.userMessage : styles.botMessage}>
      {m.content}
    </span>
  </div>
))}

</div>

<div style={styles.inputArea}>
<input style={styles.input} placeholder="ጥያቄዎን እዚህ ይጻፉ..." />
<button style={styles.button}>ላክ</button>
</div>
</div>
);
};

export default App;

## Overview

App Hosting provides configuration-free build and deploy support for Web apps developed in these frameworks:

* Next.js 13+
* Angular 17.2+

This repo holds the code for the adapters that enable support for these frameworks. At a high level these adapters transform framework specific configurations into an [output bundle spec](#app-hosting-output-bundle) that App Hosting can use to configure frameworks support. For more information see [Framework integration](https://firebase.google.com/docs/app-hosting/about-app-hosting#frameworks).

## App Hosting output bundle

The App Hosting output bundle is a file based specification that allows different frameworks to configure and customize their App Hosting deployment for enhanced support.

Any framework that can generate a build output in accordance with the App Hosting output bundle can be deployed on App Hosting.

The output bundle primarily consists of a `bundle.yaml` file that sits inside of the `.apphosting` directory. This bundle.yaml contains all the ways that frameworks can configure App Hosting when users deploy their applications.

> [!NOTE]
> App Hosting technically supports all node applications, but no custom framework features will be enabled without the output bundle.
## Output bundle Schema

The output bundle is contained in a single file:

```shell
.apphosting/bundle.yaml
```

As long as this file exists and follows the schema, App Hosting will be able to process the build properly.

The schema can also be found in [source](https://github.com/FirebaseExtended/firebase-framework-tools/blob/main/packages/%40apphosting/common/src/index.ts#L4)

```typescript
interface OutputBundle {
version: "v1"
runConfig: RunConfig;
metadata: Metadata;
outputFiles?: OutputFiles;
}
```

### Version

The `version` represents which output bundle version is currently being used. The current version is v1.

### RunConfig

The `runConfig` fields configures the Cloud Run service associated with the App Hosting Backend.

```typescript
interface RunConfig {
runCommand: string;
environmentVariables?: EnvVarConfig[];
concurrency?: number;
cpu?: number;
memoryMiB?: number;
minInstances?: number;
maxInstances?: number;
}
```

| Field | Type | Description | Required? |
| ---------- | ------- | - | - |
| `runCommand` | `string` |Command to start the server (e.g. `node dist/index.js`). Assume this command is run from the root dir of the workspace. This should be the productionized version of the server start command. | y |
| `environmentVariables`| `EnvVarConfig[]` | Environment variables present in the server execution environment.| n |
| `concurrency` | `number` | The maximum number of concurrent requests that each server instance can receive.| n |
| `cpu` | `number` |The number of CPUs used in a single server instance. | n |
| `memoryMiB` | `number` | The amount of memory available for a server instance.| n |
| `minInstance` | `number` |The limit on the minimum number of function instances that may coexist at a given time. | n |
| `MaxInstance` | `number` | The limit on the maximum number of function instances that may coexist at a given time.| n |

Many of these fields are shared with `apphosting.yaml`. See the [runConfig reference documentation](https://firebase.google.com/docs/reference/apphosting/rest/v1beta/projects.locations.backends.builds#runconfig) for additional context and default values.

### EnvVarConfig

```typescript
interface EnvVarConfig {
variable: string;
value: string;
availability: 'RUNTIME'
}

```

| Field | Type | Description | Required? |
| ---------- | ------- | - | - |
| `variable` | `string` |Name of the environment variable | y |
| `value` | `string` |Value associated with the environment variable | y |
| `availability` | `RUNTIME` | Where the variable will be available. For now this will always be `RUNTIME` | y |

### Metadata

```typescript
interface Metadata {
adapterPackageName: string;
adapterVersion: string;
framework: string;
frameworkVersion?: string;
}

```

| Field | Type | Description | Required? |
| ---------- | ------- | - | - |
| `adapterPackageName` | `string` |Name of the adapter (this should be the npm package name) | y |
| `adapterVersion`| `string` | Version of the adapter | y |
| `framework` | `string` | Name of the framework that is being supported | y |
| `frameworkVersion` | `string` |Version of the framework that is being supported | n |

### OutputFiles

OutputFiles is an optional field to configure outputFiles and optimize server files + static assets.

```typescript
interface OutputFiles {
serverApp: ServerApp
}

```

| Field | Type | Description | Required? |
| ---------- | ------- | - | - |
| `serverApp` | `ServerApp` | ServerApp holds configurations related to the serving files at runtime from Cloud Run | y |

### ServerApp

OutputFiles is an optional field to configure outputFiles and optimize server files + static assets.

```typescript
interface ServerApp {
include: string[]
}

```

| Field | Type | Description | Required? |
| ---------- | ------- | - | - |
| `include` | `string[]` | include holds a list of directories + files relative to the app root dir that frameworks need to deploy to the App Hosting server, generally this will be the output/dist directory (e.g. .output or dist). In the case that the framework wants to include all files they can use [“.”] | y |

## Sample

Here is a sample `.apphosting/bundle.yaml` file putting all this together:

```yaml
version: v1
runConfig:
runCommand: node dist/index.js
environmentVariables:
- variable: VAR
value: 8080
availability: RUNTIME
concurrency: 80
cpu: 2
memoryMiB: 512
minInstances: 0
maxInstances: 14

outputFiles:
serverApp:
include:
- dist
- .output

metadata:
adapterPackageName: npm-name
adapterVersion: 12.0.0
framework: framework-name
frameworkVersion: 1.0.0
```
As long as you have the `bundle.yaml` in this format, App Hosting will be able to deploy any framework that supports server side rendering.