Skip to main content

A Python web framework inspired by Next.js with file-based routing, SSR, and SSG and more

Project description

๐Ÿš€ NextPy Framework

The Python web framework with exact Next.js syntax! Build modern web applications with file-based routing, JSX-like components, React-like hooks, server-side rendering (SSR), static site generation (SSG), and more - all with the same developer experience as Next.js but in Python!

โœจ What's New - Complete Next.js Experience!

NextPy now provides identical Next.js syntax in Python with three different syntax styles:

๐ŸŽฏ True JSX Syntax (NEW!)

Write exact Next.js JSX syntax in Python:

// pages/index.py - Exact Next.js syntax!
def Home(message):
  return (
    <div className="container">
      <h1>{message}</h1>
      <p>Welcome to NextPy!</p>
      <button onClick="alert('Hello!')">Click Me</button>
    </div>
  );

def getServerSideProps(context):
  return {
    'props': {
      'message': 'Hello from JSX!'
    }
  }

default = Home

๐Ÿงฉ Component-Style (Python Functions)

Traditional Python function components:

from nextpy.components import Button, Card, Input

def Home(props):
    message = props.get('message', 'Hello!')
    
    return div({'className': 'container'},
        h1({}, message),
        Button(text="Click Me", variant="primary"),
        Input(name="email", placeholder="Enter email")
    )

๐Ÿ“„ Template Style (Traditional)

Jinja2 templates with data fetching:

def get_template():
    return "home.html"

async def get_server_side_props(context):
    return {
        "props": {
            "message": "Hello from templates!"
        }
    }

๐ŸŽฃ React-Like Hooks (NEW!)

All your favorite React hooks now work in Python:

from nextpy import useState, useEffect, with_hooks

@with_hooks
def Counter(props):
  [count, setCount] = useState(0)
  [name, setName] = useState('NextPy')
  
  useEffect(() => {
    print(f'Count changed to: {count}')
  }, [count])
  
  return (
    <div>
      <h1>Hello {name}!</h1>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );

๐ŸŽจ Complete Component Library (NEW!)

50+ pre-built components for rapid development:

from nextpy.components import (
    Button, Card, Input, Form, Select,
    Navbar, Tabs, Dropdown, Modal,
    Alert, Badge, Progress, Avatar,
    Container, Grid, Flex, Stack
)

def MyPage():
  return (
    <div>
      <Navbar brand="My App" />
      <Container>
        <Card title="Welcome">
          <Form>
            <Input name="email" placeholder="Enter email" />
            <Button text="Submit" variant="primary" />
          </Form>
        </Card>
      </Container>
    </div>
  );

๐ŸŽ‰ Demo Mode (NEW!)

When users install NextPy and run nextpy dev without creating a project, they get beautiful built-in documentation and examples:

pip install nextpy-framework
nextpy dev  # No project needed!

Demo Mode includes:

  • ๐Ÿ  Beautiful homepage with NextPy showcase
  • ๐Ÿ“š Complete documentation hub
  • ๐ŸŽจ Component library demonstrations
  • ๐ŸŽฃ Hooks examples and tutorials
  • ๐Ÿš€ Project creation interface

๐Ÿš€ Features

๐ŸŽฏ Next.js-Style Components

  • True JSX Syntax - Write <div> tags directly in Python
  • Component Functions - Same structure as Next.js
  • Props & Children - Pass props and children exactly like Next.js
  • Default Exports - Use default = Component just like Next.js
  • 50+ Components - Pre-built UI components (Button, Card, Form, etc.)
  • React-like Hooks - useState, useEffect, useReducer, useContext, etc.

๐Ÿงฉ Core Next.js Features

  • File-based Routing - Pages in pages/ become routes automatically
  • Dynamic Routes - [slug].py creates dynamic URL segments
  • Server-Side Rendering - getServerSideProps fetches data per request
  • Static Site Generation - getStaticProps builds pages at compile time
  • API Routes - Create API endpoints in pages/api/ with HTTP method handlers

๐ŸŽฃ React-Like Hooks

  • useState - State management: [count, setCount] = useState(0)
  • useEffect - Side effects and lifecycle
  • useReducer - Complex state management
  • useContext - Global state sharing
  • useRef - Mutable references
  • useMemo - Memoized values
  • useCallback - Memoized functions
  • Custom Hooks - useCounter, useToggle, useLocalStorage, useFetch, useDebounce

๐ŸŽจ Development Experience

  • Hot Reload - Instant updates on file changes during development
  • Demo Mode - Built-in documentation when no project exists
  • Dual Rendering - Choose between JSX, component, or template rendering
  • Type Safety - Full Python type hints support
  • Error Handling - Comprehensive error messages and debugging

๐Ÿ› ๏ธ Traditional Features (Still Supported)

  • HTMX Integration - SPA-like navigation without heavy JavaScript
  • Jinja2 Templates - Powerful templating with layout inheritance
  • Tailwind CSS Integration - Utility-first CSS framework
  • Database Integration - SQLAlchemy and database utilities

๐Ÿ“ฆ Installation

Via pip

pip install nextpy-framework

Development Install (Editable)

git clone https://github.com/nextpy/nextpy-framework.git
cd nextpy-framework
pip install -e .

๐Ÿš€ Quick Start

1. Create a new project

nextpy create my-app
cd my-app

2. Start development server

nextpy dev

3. Open your browser

Navigate to http://localhost:5000 to see your app!

๐Ÿ“ Project Structure

my-app/
โ”œโ”€โ”€ pages/                 # File-based routing
โ”‚   โ”œโ”€โ”€ index.py          # Homepage (/)
โ”‚   โ”œโ”€โ”€ about.py          # About page (/about)
โ”‚   โ”œโ”€โ”€ [slug].py         # Dynamic routes (/:slug)
โ”‚   โ””โ”€โ”€ api/              # API routes
โ”‚       โ”œโ”€โ”€ users.py      # API endpoint (/api/users)
โ”‚       โ””โ”€โ”€ posts.py      # API endpoint (/api/posts)
โ”œโ”€โ”€ components/           # Reusable components (NEW!)
โ”‚   โ”œโ”€โ”€ ui/              # Basic UI components
โ”‚   โ”‚   โ”œโ”€โ”€ Button.jsx
โ”‚   โ”‚   โ”œโ”€โ”€ Card.jsx
โ”‚   โ”‚   โ””โ”€โ”€ Modal.jsx
โ”‚   โ”œโ”€โ”€ forms/            # Form components
โ”‚   โ”‚   โ”œโ”€โ”€ Form.jsx
โ”‚   โ”‚   โ””โ”€โ”€ Input.jsx
โ”‚   โ”œโ”€โ”€ layout/           # Layout components
โ”‚   โ”‚   โ”œโ”€โ”€ Header.jsx
โ”‚   โ”‚   โ”œโ”€โ”€ Footer.jsx
โ”‚   โ”‚   โ””โ”€โ”€ Sidebar.jsx
โ”‚   โ””โ”€โ”€ features/        # Feature-specific components
โ”‚       โ”œโ”€โ”€ DataTable.jsx
โ”‚       โ””โ”€โ”€ Chart.jsx
โ”œโ”€โ”€ templates/            # Jinja2 templates (optional)
โ”œโ”€โ”€ public/              # Static files
โ”œโ”€โ”€ main.py              # Application entry point
โ””โ”€โ”€ requirements.txt     # Python dependencies

๐Ÿ“ฆ Framework Structure

.nextpy_framework/nextpy/
โ”œโ”€โ”€ __init__.py           # Main exports and imports
โ”œโ”€โ”€ hooks.py              # React-like hooks implementation
โ”œโ”€โ”€ hooks_provider.py     # Hooks integration with components
โ”œโ”€โ”€ jsx.py                # JSX element system
โ”œโ”€โ”€ jsx_preprocessor.py   # JSX to Python transformer
โ”œโ”€โ”€ true_jsx.py          # True JSX parser
โ”œโ”€โ”€ components/           # Component library
โ”‚   โ”œโ”€โ”€ __init__.py       # Component exports
โ”‚   โ”œโ”€โ”€ form.py           # Form components
โ”‚   โ”œโ”€โ”€ layout.py         # Layout components
โ”‚   โ”œโ”€โ”€ ui.py             # UI components
โ”‚   โ””โ”€โ”€ navigation.py     # Navigation components
โ”œโ”€โ”€ core/
โ”‚   โ”œโ”€โ”€ router.py         # Main routing system
โ”‚   โ”œโ”€โ”€ component_router.py # Component rendering
โ”‚   โ”œโ”€โ”€ renderer.py       # Template rendering
โ”‚   โ”œโ”€โ”€ demo_router.py    # Demo mode routing
โ”‚   โ””โ”€โ”€ demo_pages_simple.py # Demo pages
โ””โ”€โ”€ server/
    โ””โ”€โ”€ app.py            # FastAPI application

๐ŸŽฏ Syntax Examples

True JSX Syntax (Recommended)

// pages/index.py
def HomePage():
  return (
    <div className="container">
      <header>
        <h1>Welcome to NextPy!</h1>
        <p>Building modern web apps with Python!</p>
      </header>
      
      <main>
        <section>
          <h2>Features</h2>
          <ul>
            <li>File-based routing</li>
            <li>Server-side rendering</li>
            <li>React-like hooks</li>
            <li>50+ components</li>
          </ul>
        </section>
        
        <section>
          <h2>Get Started</h2>
          <button onClick="alert('Hello!')">Click Me</button>
        </section>
      </main>
    </div>
  );

def getServerSideProps(context):
  return {
    'props': {}
  }

default = HomePage

Component-Style with Hooks

// pages/counter.py
from nextpy import useState, useEffect, with_hooks
from nextpy.components import Button, Card

@with_hooks
def CounterPage():
  [count, setCount] = useState(0)
  [message, setMessage] = useState('Click the button!')
  
  useEffect(() => {
    if count > 0:
      setMessage(f'Button clicked {count} times!')
  }, [count])
  
  return (
    <div className="container">
      <Card title="Counter Demo">
        <h2>{message}</h2>
        <p>Count: {count}</p>
        <Button 
          text="Increment" 
          onClick={() => setCount(count + 1)}
          variant="primary"
        />
      </Card>
    </div>
  );

API Routes

// pages/api/users.py
def get(request):
  return {
    'users': [
      {'id': 1, 'name': 'John Doe', 'email': 'john@example.com'},
      {'id': 2, 'name': 'Jane Smith', 'email': 'jane@example.com'}
    ]
  }

def post(request):
  data = request.get('json', {})
  return {
    'user': data,
    'created': True
  }, 201

default = lambda request: {
    'GET': get,
    'POST': post
  }.get(request.get('method', 'GET'), lambda: {'error': 'Method not allowed'})(request)

๐Ÿงฉ Reusable JSX Components

Creating Reusable Components

Create reusable JSX components in the components/ directory:

// components/ui/Button.jsx
def Button(text, variant="primary", size="medium", onClick=None, className=""):
  base_classes = "px-4 py-2 rounded-md font-medium transition-colors"
  
  variant_classes = {
    "primary": "bg-blue-600 text-white hover:bg-blue-700",
    "secondary": "bg-gray-200 text-gray-900 hover:bg-gray-300",
    "danger": "bg-red-600 text-white hover:bg-red-700"
  }
  
  size_classes = {
    "small": "px-2 py-1 text-sm",
    "medium": "px-4 py-2 text-sm",
    "large": "px-6 py-3 text-base"
  }
  
  classes = f"{base_classes} {variant_classes[variant]} {size_classes[size]} {className}"
  
  return (
    <button className={classes} onClick={onClick}>
      {text}
    </button>
  );
// components/ui/Card.jsx
def Card(title, children, className=""):
  return (
    <div className={"bg-white rounded-lg shadow-md p-6 " + className}>
      <h2 className="text-xl font-bold mb-4">{title}</h2>
      {children}
    </div>
  );

Using Reusable Components

// pages/index.py
from components.ui.Button import Button
from components.ui.Card import Card

def HomePage():
  return (
    <div className="container mx-auto p-8">
      <Card title="Welcome">
        <p>This is a reusable card component!</p>
        <Button 
          text="Click Me" 
          variant="primary"
          onClick={() => alert('Button clicked!')}
        />
      </Card>
    </div>
  );

Advanced Reusable Components

// components/layout/Header.jsx
def Header(title, links):
  return (
    <header className="bg-white shadow-sm">
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <div className="flex justify-between items-center h-16">
          <h1 className="text-2xl font-bold">{title}</h1>
          <nav>
            {links.map(link => (
              <a 
                key={link.href}
                href={link.href}
                className="text-gray-700 hover:text-gray-900 px-3 py-2 rounded-md text-sm font-medium"
              >
                {link.label}
              </a>
            ))}
          </nav>
        </div>
      </div>
    </header>
  );

Component Organization Best Practices

components/
โ”œโ”€โ”€ ui/              # Basic UI components
โ”‚   โ”œโ”€โ”€ Button.jsx
โ”‚   โ”œโ”€โ”€ Card.jsx
โ”‚   โ””โ”€โ”€ Modal.jsx
โ”œโ”€โ”€ forms/            # Form components
โ”‚   โ”œโ”€โ”€ Form.jsx
โ”‚   โ””โ”€โ”€ Input.jsx
โ”œโ”€โ”€ layout/           # Layout components
โ”‚   โ”œโ”€โ”€ Header.jsx
โ”‚   โ”œโ”€โ”€ Footer.jsx
โ”‚   โ””โ”€โ”€ Sidebar.jsx
โ””โ”€โ”€ features/        # Feature-specific components
    โ”œโ”€โ”€ DataTable.jsx
    โ””โ”€โ”€ Chart.jsx

๐Ÿ  Default Export Pattern

What export = Home Means

In NextPy (like Next.js), export = Home specifies the default export of a module. This tells NextPy which component to render for a page.

Page Component Structure

// pages/index.py

# 1. Define your component function
def Home(props):
  message = props.get('message', 'Hello World!')
  return (
    <div>
      <h1>{message}</h1>
      <p>Welcome to NextPy!</p>
    </div>
  );

# 2. Define data fetching function (optional)
def getServerSideProps(context):
  return {
    'props': {
      'message': 'Hello from Server!'
    }
  }

# 3. Set default export - THIS IS THE PAGE COMPONENT
export = Home

Alternative Syntax

// pages/index.py
def Home(props):
  return <div>Hello World!</div>

def getServerSideProps(context):
  return {'props': {}}

# Alternative way to set default export
default = Home

Why Default Export Matters

  1. Page Identification: NextPy uses this to identify the main page component
  2. Rendering: When someone visits /, NextPy renders the Home component
  3. Data Flow: Props from getServerSideProps are passed to the Home component
  4. Consistency: Matches Next.js pattern exactly

Complete Example: Reusable Components + Default Export

// pages/index.py
from components.layout.Header import Header
from components.layout.Footer import Footer
from components.ui.Button import Button
from components.ui.Card import Card

def Home(props):
  user = props.get('user', {'name': 'Guest'})
  
  return (
    <div>
      <Header 
        title="NextPy App"
        links={[
          {'label': 'Home', 'href': '/'},
          {'label': 'About', 'href': '/about'},
          {'label': 'Contact', 'href': '/contact'}
        ]}
      />
      
      <main className="container mx-auto p-8">
        <Card title={`Welcome ${user['name']}!`}>
          <p>This is NextPy with reusable JSX components!</p>
          <Button 
            text="Get Started" 
            variant="primary"
            onClick={() => alert('Welcome to NextPy!')}
          />
        </Card>
      </main>
      
      <Footer />
    </div>
  );

def getServerSideProps(context):
  return {
    'props': {
      'user': {'name': 'John Doe'}
    }
  }

export = Home  # This is the page component!

File Structure with Default Exports

pages/
โ”œโ”€โ”€ index.py          # export = Home (renders at /)
โ”œโ”€โ”€ about.py          # export = About (renders at /about)
โ”œโ”€โ”€ contact.py         # export = Contact (renders at /contact)
โ”œโ”€โ”€ [slug].py         # export = Post (renders at /:slug)
โ””โ”€โ”€ api/
    โ”œโ”€โ”€ users.py      # No default export (API route)
    โ””โ”€โ”€ posts.py      # No default export (API route)

๐ŸŽฃ Hooks Reference

Core Hooks

# State management
[count, setCount] = useState(0)

# Side effects
useEffect(() => {
  console.log('Component mounted')
  return () => console.log('Cleanup')
}, [])

# Complex state
def counterReducer(state, action):
  if action['type'] == 'increment':
    return {'count': state['count'] + 1}
  return state

[state, dispatch] = useReducer(counterReducer, {'count': 0})

# Context
ThemeContext = createContext('theme', 'light')
theme = useContext(ThemeContext)

# Mutable refs
inputRef = useRef()

# Memoization
expensiveValue = useMemo(() => calculateExpensiveValue(data), [data])

# Memoized callbacks
handleClick = useCallback(() => setCount(count + 1), [])

Custom Hooks

# Counter with increment/decrement
[count, increment, decrement] = useCounter(10)

# Toggle boolean values
[visible, toggle] = useToggle(true)

# LocalStorage persistence
[value, setValue] = useLocalStorage('key', 'default')

# API data fetching
data = useFetch('/api/users')

# Debounced values
debouncedSearch = useDebounce(searchTerm, 500)

๐ŸŽจ Components Reference

Form Components

Input(name="email", type="email", placeholder="Enter email")
TextArea(name="message", placeholder="Your message", rows=4)
Select(name="country", options=[
  {'label': 'USA', 'value': 'us'},
  {'label': 'Canada', 'value': 'ca'}
])
Checkbox(name="newsletter", label="Subscribe to newsletter")
RadioGroup(name="contact", options=[
  {'label': 'Email', 'value': 'email'},
  {'label': 'Phone', 'value': 'phone'}
])
Form(action="/submit", method="POST", children=[...])
SubmitButton(text="Submit Form")

UI Components

Button(text="Click Me", variant="primary", size="large")
Badge(text="New", variant="success")
Avatar(size="medium", fallback="JD")
Alert(message="Success!", variant="success")
Progress(value={75}, variant="primary")
Skeleton(variant="text")
Tooltip(text="Hover info", children=[...])
Chip(text="Removable", removable=True)

Layout Components

Container(max_width="6xl", children=[...])
Grid(columns=3, gap=4, children=[...])
Flex(direction="row", justify="center", children=[...])
Stack(direction="vertical", spacing=4, children=[...])
Card(title="Title", children=[...])

Navigation Components

Navbar(brand="My App", menu_items=[...])
Tabs(tabs=[...], active_tab="tab1")
Dropdown(trigger="Menu", items=[...])
Pagination(current_page=3, total_pages=10)
SearchBar(placeholder="Search...")
BreadcrumbNav(items=[...])

๐Ÿ”ง Data Fetching

Server-Side Rendering

// pages/posts/[slug].py
async def getServerSideProps(context):
  slug = context.get('params', {}).get('slug')
  
  # Fetch data from database or API
  post = await fetch_post_by_slug(slug)
  
  if not post:
    return {'notFound': True}
  
  return {
    'props': {
      'post': post
    }
  }

def PostPage(props):
  post = props.get('post')
  return (
    <div>
      <h1>{post['title']}</h1>
      <div>{post['content']}</div>
    </div>
  );

Static Site Generation

// pages/blog/index.py
async def getStaticProps(context):
  posts = await fetch_all_posts()
  
  return {
    'props': {
      'posts': posts
    },
    'revalidate': 3600  # Revalidate every hour
  }

def BlogIndex(props):
  posts = props.get('posts', [])
  return (
    <div>
      <h1>Blog Posts</h1>
      {posts.map(post => (
        <article key={post['id']}>
          <h2>{post['title']}</h2>
          <p>{post['excerpt']}</p>
        </article>
      ))}
    </div>
  );

Static Paths

// pages/blog/[slug].py
async def getStaticPaths(context):
  posts = await fetch_all_posts()
  
  return {
    'paths': [
      {'params': {'slug': post['slug']}}
      for post in posts
    ],
    'fallback': 'blocking'
  }

๐Ÿš€ CLI Commands

Project Creation

# Create new project
nextpy create my-app

# Create project with specific template
nextpy create my-app --template blog

# Create project in current directory
nextpy create .

Development

# Start development server
nextpy dev

# Start on specific port
nextpy dev --port 3000

# Start with debug mode
nextpy dev --debug

Build & Deploy

# Build for production
nextpy build

# Build static site
nextpy build --static

# Export to static files
nextpy export

Database

# Initialize database
nextpy db init

# Run migrations
nextpy db migrate

# Create migration
nextpy db migration create add_users_table

๐ŸŽฏ Demo Mode

When you install NextPy and run nextpy dev without creating a project:

pip install nextpy-framework
nextpy dev

You'll see:

  • ๐ŸŽ‰ Demo Mode Activated message
  • ๐Ÿ  Beautiful homepage showcasing NextPy features
  • ๐Ÿ“š Complete documentation with examples
  • ๐ŸŽจ Component demonstrations with live code
  • ๐ŸŽฃ Hooks tutorials and API reference
  • ๐Ÿš€ Project creation interface

๐Ÿ“š Documentation

Getting Started

Advanced Topics

API Reference

๐Ÿงช Testing

Run Tests

# Run all tests
pytest

# Run specific test file
pytest test_components.py

# Run with coverage
pytest --cov=nextpy_framework

Test Examples

# test_components.py
from nextpy.components import Button, Card

def test_button_component():
    button = Button(text="Click Me", variant="primary")
    assert button is not None

def test_card_component():
    card = Card(title="Test", children=["Content"])
    assert card is not None

๐Ÿš€ Deployment

Production Build

# Build for production
nextpy build

# Start production server
nextpy start

Docker

FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .
RUN nextpy build

EXPOSE 5000
CMD ["nextpy", "start"]

Vercel

{
  "version": 2,
  "builds": [
    {
      "src": "main.py",
      "use": "@vercel/python"
    }
  ]
}

๐Ÿค Contributing

We welcome contributions! Please see CONTRIBUTING.md for details.

Development Setup

# Clone repository
git clone https://github.com/nextpy/nextpy-framework.git
cd nextpy-framework

# Install in development mode
pip install -e .

# Run tests
pytest

# Run development server
python main.py

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿ™ Acknowledgments

  • Next.js Team - For the amazing framework that inspired NextPy
  • React Team - For the hooks and component patterns
  • FastAPI - For the excellent ASGI framework
  • Python Community - For the amazing ecosystem

๐ŸŽ‰ What's Next?

  • ๐Ÿš€ More Components - Expanding the component library
  • ๐ŸŽจ Theme System - Built-in theming support
  • ๐Ÿ“ฑ Mobile App - React Native integration
  • ๐Ÿ”Œ Plugin System - Extensible plugin architecture
  • ๐ŸŒ Internationalization - Multi-language support

NextPy: Next.js for Python ๐Ÿ โ†’ React โค๏ธ Python

Build modern web applications with the exact Next.js experience, but in Python! ๐Ÿš€

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nextpy_framework-2.3.1.tar.gz (116.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nextpy_framework-2.3.1-py3-none-any.whl (131.2 kB view details)

Uploaded Python 3

File details

Details for the file nextpy_framework-2.3.1.tar.gz.

File metadata

  • Download URL: nextpy_framework-2.3.1.tar.gz
  • Upload date:
  • Size: 116.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for nextpy_framework-2.3.1.tar.gz
Algorithm Hash digest
SHA256 efa0008cb26c09c014cbeef1eabde12827a4d3d20eface3185d0a80758ffeff2
MD5 c0d88fc23dca2eb18dc4193ae024f824
BLAKE2b-256 726dcd35287d7079515fe386f68392d695c39197803647aa7b788b3d0bc98fd2

See more details on using hashes here.

File details

Details for the file nextpy_framework-2.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for nextpy_framework-2.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 61ab6d57bb974d086fef060b24d7b08a56619e12e933b40a409c8083ca1442f6
MD5 bcbe02728706ecdc8fd9f8635d3409c6
BLAKE2b-256 36c53b47254b064932c47b3de531074a3279c90a93bd624f7fbfc08756814a12

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page