GraphQL Schema Designer
Run ID: 69cb13f042bc43f7e3be73502026-03-31Development
PantheraHive BOS
BOS Dashboard

This deliverable outlines a complete GraphQL schema design for a blogging platform, including its type definitions, queries, mutations, subscriptions, resolver implementations, and integration examples for both server-side (Node.js with Apollo Server) and client-side (React with Apollo Client). This design aims for clarity, scalability, and adherence to best practices, providing a robust foundation for your application.


1. Introduction to the GraphQL Schema Design

This document provides a comprehensive GraphQL schema for a blogging platform. The schema defines the structure of data that can be queried, modified, and subscribed to by clients. We'll cover core entities such as User, Post, Comment, and Tag, along with their relationships and associated operations.

Key Features of this Design:

2. GraphQL Schema Definition Language (SDL)

The following is the complete schema defined using GraphQL's Schema Definition Language (SDL).

text • 971 chars
## 3. Resolver Structure and Implementation Examples

Resolvers are functions that determine how to fetch the data for a particular field in your schema. Each field in your schema's types (Query, Mutation, Subscription, and custom types like `User`, `Post`) needs a corresponding resolver function.

A resolver typically takes four arguments: `(parent, args, context, info)`.

*   `parent`: The result of the parent resolver. Useful for nested fields (e.g., `Post.author` resolver receives the `Post` object as `parent`).
*   `args`: An object containing all the arguments provided to the field (e.g., `id` for `user(id: ID!)`).
*   `context`: An object shared across all resolvers in a single GraphQL operation. Used for database connections, authentication info, etc.
*   `info`: Contains execution state information, including the parsed AST of the query.

### 3.1. Custom Scalar Resolver

For `DateTime`, we need to define how to serialize, parse, and validate it.

Sandboxed live preview

GraphQL Schema Designer: Architectural Planning Study Plan

This document outlines a comprehensive study plan designed to equip you with the foundational knowledge and practical skills required for robust GraphQL schema architecture and design. This plan is tailored to prepare you for effectively executing the subsequent steps of the "GraphQL Schema Designer" workflow, ensuring you can confidently design, implement, and maintain high-quality GraphQL APIs.

1. Introduction and Purpose

This study plan focuses on the architectural planning phase of GraphQL schema design. Before diving into specific types, queries, and mutations, it's crucial to understand the underlying principles, best practices, and strategic considerations that lead to a scalable, maintainable, and performant GraphQL API. This plan will guide you through core GraphQL concepts, advanced schema design patterns, and critical architectural decisions.

2. Learning Objectives

Upon completion of this study plan, you will be able to:

  • Understand Core GraphQL Concepts: Articulate the fundamental differences between GraphQL and REST, and explain the roles of types, fields, queries, mutations, and subscriptions.
  • Design Effective Data Models: Translate business requirements into a logical and efficient GraphQL schema, defining types, relationships, and data structures.
  • Apply Schema Design Principles: Utilize best practices for schema evolution, naming conventions, pagination, filtering, and error handling.
  • Implement Advanced Schema Features: Leverage interfaces, unions, directives, and input types to create flexible and powerful schemas.
  • Architect Resolver Strategies: Understand different approaches to data fetching and resolution, including database integrations, microservices, and third-party APIs.
  • Consider Performance and Security: Identify and mitigate common performance bottlenecks and security vulnerabilities in GraphQL APIs.
  • Utilize GraphQL Tooling: Become proficient with essential tools for schema introspection, documentation, and development.

3. Weekly Schedule

This 4-week study plan provides a structured approach to learning. Each week builds upon the previous, culminating in a holistic understanding of GraphQL schema architecture.

Week 1: GraphQL Fundamentals & Core Concepts

  • Objective: Grasp the basics of GraphQL, its advantages, and fundamental building blocks.
  • Topics:

* Introduction to GraphQL: Why GraphQL? Comparison with REST.

* Client-Server Interaction: Queries, Mutations, Variables.

* Schema Definition Language (SDL): Basic Type System (Scalar Types, Object Types).

* Fields, Arguments, Aliases, Fragments.

* Introduction to Resolvers: How data is fetched.

* Setting up a basic "Hello World" GraphQL server (e.g., Apollo Server, Express-GraphQL).

  • Activities:

* Read official GraphQL documentation.

* Complete a basic GraphQL tutorial (e.g., "How to GraphQL").

* Set up and run a simple GraphQL server locally.

* Practice writing basic queries and mutations using a GraphQL client (e.g., GraphiQL, Apollo Studio Explorer).

Week 2: Schema Design Principles & Data Modeling

  • Objective: Learn to design robust and intuitive GraphQL schemas, focusing on data modeling and relationships.
  • Topics:

* Designing Object Types: Best practices for fields, nullable vs. non-nullable.

* Relationships: One-to-one, one-to-many, many-to-many.

* Input Types: Handling complex input for mutations.

* Enums: Defining a set of allowed values.

* Custom Scalar Types: When and how to create them.

* Pagination Strategies: Cursor-based vs. Offset-based.

* Filtering and Sorting: Designing arguments for data manipulation.

* Naming Conventions: Cohesion, consistency, clarity.

  • Activities:

* Design a schema for a simple application (e.g., a blog, an e-commerce product catalog) on paper or using a schema design tool.

* Implement the designed schema in your local GraphQL server.

* Practice implementing pagination, filtering, and sorting in your server.

* Refactor existing types to use Enums or custom Scalars where appropriate.

Week 3: Advanced Schema Features & Real-time Data

  • Objective: Explore advanced GraphQL features for schema flexibility and real-time capabilities.
  • Topics:

* Interfaces: Defining shared fields across multiple types.

* Unions: Returning one of several possible types.

* Directives: Extending GraphQL's capabilities (e.g., @deprecated, custom directives).

* Subscriptions: Real-time data updates (WebSockets, PubSub patterns).

* Error Handling: Designing effective error responses.

* Authentication and Authorization: Integrating security into the schema.

  • Activities:

* Refactor your application schema to utilize interfaces and unions for polymorphic data.

* Implement a custom directive (e.g., for logging or authentication).

* Add a subscription to your server (e.g., for new posts or comments).

* Design an error handling strategy for your API.

* Research and understand different auth patterns for GraphQL.

Week 4: Architectural Considerations, Best Practices & Tooling

  • Objective: Understand the broader architectural implications of GraphQL, performance, security, and essential development tools.
  • Topics:

* Schema Stitching vs. Federation: Architecting distributed GraphQL APIs.

* N+1 Problem: Identifying and solving common performance issues.

* Batching and Caching: Data Loader, client-side caching strategies.

* Security Best Practices: Depth limiting, query cost analysis, rate limiting.

* Schema Evolution: Versioning strategies, deprecation.

* Tooling: GraphQL Playground/GraphiQL, Apollo Studio, ESLint plugins, code generation.

* Testing GraphQL APIs: Unit, integration, and end-to-end testing.

  • Activities:

* Analyze your existing schema for potential N+1 problems and implement Data Loader.

* Research and compare schema stitching vs. federation for microservices architectures.

* Configure a GraphQL linter for your project.

* Explore different client-side caching mechanisms (e.g., Apollo Client, Relay).

* Develop a plan for schema evolution for a long-lived API.

4. Recommended Resources

  • Official Documentation:

* [GraphQL.org](https://graphql.org/)

* [Apollo GraphQL Docs](https://www.apollographql.com/docs/)

  • Books:

* "Learning GraphQL" by Eve Porcello & Alex Banks (O'Reilly)

* "Production-Ready GraphQL" by Marc-André Giroux (Manning)

  • Online Courses:

* Free:

* How to GraphQL ([howtographql.com](https://www.howtographql.com/)) - Excellent comprehensive free resource.

* Introduction to GraphQL (Frontend Masters, often free trials or free courses available)

* Paid:

* Advanced GraphQL (Frontend Masters)

* GraphQL with React: The Complete Developers Guide (Udemy)

* Apollo Odyssey (Apollo GraphQL's official learning platform)

  • Blogs & Articles:

* [Apollo Blog](https://www.apollographql.com/blog/)

* [Hasura Blog](https://hasura.io/blog/)

* Medium articles on GraphQL best practices.

  • Tools:

* Schema Design: GraphQL Editor, GraphQL Playground, Apollo Studio

* Development: VS Code GraphQL extension, Postman

* Client Libraries: Apollo Client, Relay, Urql

5. Milestones

Achieving these milestones will signify significant progress and understanding:

  • End of Week 1: Successfully set up a basic GraphQL server and execute all fundamental query/mutation operations against it.
  • End of Week 2: Design and implement a complete GraphQL schema for a moderate complexity application (e.g., a social media feed, a task management system) with appropriate relationships, input types, and basic pagination.
  • End of Week 3: Enhance the application schema with interfaces, unions, a custom directive, and a working subscription.
  • End of Week 4: Integrate Data Loader for performance optimization, implement basic security measures (e.g., depth limiting), and articulate a strategy for schema evolution.
  • Overall Project: Develop a "Portfolio GraphQL API" that showcases all learned concepts, complete with a well-documented schema and example client queries.

6. Assessment Strategies

Regular assessment is crucial for reinforcing learning and identifying areas for improvement.

  • Self-Assessment Quizzes: After each weekly section, create and answer questions based on the learning objectives.
  • Code Reviews: Regularly review your own code for adherence to best practices, readability, and efficiency. (If possible, engage in peer code reviews with a study partner).
  • Project-Based Learning: The weekly activities and final portfolio project serve as practical assessments of your ability to apply concepts.
  • Mock Design Challenges: Attempt to design a GraphQL schema for a new, unfamiliar application scenario under time constraints. Then, critically evaluate your design against best practices.
  • Tooling Proficiency: Demonstrate the ability to use GraphQL development tools effectively for introspection, query testing, and schema management.
  • Documentation: Create clear and concise documentation for your designed schemas, explaining types, fields, and resolver logic.

By diligently following this study plan, you will build a strong foundation in GraphQL schema architecture, preparing you to tackle complex design challenges and contribute effectively to GraphQL-powered projects.

javascript

// src/data/mockDb.js

import { v4 as uuidv4 } from 'uuid';

// Initial mock data

let users = [

{ id: 'user1', username: 'alice', email: 'alice@example.com', password: 'hashedpassword', createdAt: new Date(), updatedAt: new Date() },

{ id: 'user2', username: 'bob', email: 'bob@example.com', password: 'hashedpassword', createdAt: new Date(), updatedAt: new Date() },

];

let tags = [

{ id: 'tag1', name: 'GraphQL' },

{ id: 'tag2', name: 'Node.js' },

{ id: 'tag3', name: 'React' },

];

let posts = [

{ id: 'post1', title: 'Getting Started with GraphQL', content: 'GraphQL is a query language for your API...', status: 'PUBLISHED', authorId: 'user1', tagIds: ['tag1', 'tag2'], createdAt: new Date(), updatedAt: new Date(), publishedAt: new Date() },

{ id: 'post2', title: 'Building a Blog with React', content: 'React makes it easy to build interactive UIs...', status: 'DRAFT', authorId: 'user2', tagIds: ['tag3'], createdAt: new Date(), updatedAt: new Date(), publishedAt: null },

];

let comments = [

{ id: 'comment1', postId: 'post1', authorId: 'user2', content: 'Great article!', createdAt: new Date(), updatedAt: new Date() },

{ id: 'comment2', postId: 'post1', authorId: 'user1', content: 'Thanks Bob!', createdAt: new Date(), updatedAt: new Date() },

];

export const mockDb = {

users: {

findMany: (filter = {}) => {

let result = users;

if (filter.id) result = result.filter(u => u.id === filter.id);

return result;

},

findOne: (filter) => mockDb.users.findMany(filter)[0],

create: (data) => {

const newUser = { id: uuidv4(), ...data, createdAt: new Date(), updatedAt: new Date() };

users.push(newUser);

return newUser;

},

update: (id, data) => {

const index = users.findIndex(u => u.id === id);

if (index === -1) return null;

users[index] = { ...users[index], ...data, updatedAt: new Date() };

return users[index];

},

delete: (id) => {

const initialLength = users.length;

users = users.filter(u => u.id !== id);

return users.length < initialLength;

}

},

posts: {

findMany: (filter = {}) => {

let result = posts;

if (filter.status) result = result.filter(p => p.status === filter.status);

if (filter.tagId) result = result.filter(p => p.tagIds.includes(filter.tagId));

if (filter.search) result = result.filter(p => p.title.includes(filter.search) || p.content.includes(filter.search));

if (filter.authorId) result = result.filter(p => p.authorId === filter.authorId);

return result;

},

findOne: (filter) => mockDb.posts.findMany(filter)[0],

create: (data) => {

const newPost = { id: uuidv4(), ...data, createdAt: new Date(), updatedAt: new Date() };

posts.push(newPost);

return newPost;

},

update: (id, data) => {

const index = posts.findIndex(p => p.id === id);

if (index === -1) return null;

posts[index] = { ...posts[index], ...data, updatedAt: new Date() };

return posts[index];

},

delete

gemini Output

This document outlines a comprehensive GraphQL schema design for a blogging platform, encompassing types, queries, mutations, subscriptions, resolver logic, and integration examples. It is designed to be a robust, scalable, and intuitive API for managing blog content, users, and interactions.


1. Introduction to the GraphQL Schema Design

This deliverable provides a detailed blueprint for your GraphQL API, focusing on a blogging platform use case. The goal is to establish a clear, strongly-typed, and flexible schema that enables efficient data fetching and manipulation for various client applications. We will cover the core entities, their relationships, the operations available, and how to implement and integrate them.

2. Core Principles of the Schema Design

Our GraphQL schema design adheres to the following principles:

  • Strong Typing: Every field and argument has a defined type, ensuring data consistency and enabling powerful tooling for both client and server.
  • Introspection: The schema is self-documenting, allowing clients to discover available types, fields, and operations dynamically.
  • Hierarchical Structure: The schema reflects the natural relationships between data entities, allowing clients to query nested data in a single request.
  • Client-Driven Data Fetching: Clients specify exactly what data they need, reducing over-fetching and under-fetching issues common in REST APIs.
  • Extensibility: The schema is designed to be easily extendable, allowing for the addition of new fields, types, and operations without breaking existing clients.
  • Readability and Maintainability: Using GraphQL Schema Definition Language (SDL) ensures clarity and ease of understanding for developers.

3. GraphQL Schema Definition Language (SDL)

The following SDL defines the complete schema for our blogging platform.


# Custom Scalar for Date/Time
scalar DateTime

# User Type
type User {
  id: ID!
  username: String!
  email: String!
  posts(limit: Int = 10, offset: Int = 0): [Post!]!
  comments(limit: Int = 10, offset: Int = 0): [Comment!]!
  createdAt: DateTime!
  updatedAt: DateTime!
}

# Post Type
type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  categories: [Category!]!
  tags: [Tag!]!
  comments(limit: Int = 10, offset: Int = 0): [Comment!]!
  createdAt: DateTime!
  updatedAt: DateTime!
  published: Boolean!
}

# Comment Type
type Comment {
  id: ID!
  content: String!
  author: User!
  post: Post!
  createdAt: DateTime!
  updatedAt: DateTime!
}

# Category Type
type Category {
  id: ID!
  name: String!
  description: String
  posts(limit: Int = 10, offset: Int = 0): [Post!]!
}

# Tag Type
type Tag {
  id: ID!
  name: String!
  posts(limit: Int = 10, offset: Int = 0): [Post!]!
}

# Input Types for Mutations

input CreateUserInput {
  username: String!
  email: String!
  password: String! # In a real app, this would be hashed server-side
}

input UpdateUserInput {
  username: String
  email: String
  password: String
}

input CreatePostInput {
  title: String!
  content: String!
  categoryIds: [ID!]!
  tagNames: [String!]
  published: Boolean = false
}

input UpdatePostInput {
  title: String
  content: String
  categoryIds: [ID!]
  tagNames: [String!]
  published: Boolean
}

input CreateCommentInput {
  content: String!
  postId: ID!
}

input UpdateCommentInput {
  content: String!
}

input CreateCategoryInput {
  name: String!
  description: String
}

input UpdateCategoryInput {
  name: String
  description: String
}

input CreateTagInput {
  name: String!
}

input UpdateTagInput {
  name: String!
}

# Root Query Type
type Query {
  # User Queries
  users(limit: Int = 10, offset: Int = 0): [User!]!
  user(id: ID!): User

  # Post Queries
  posts(
    published: Boolean
    categoryId: ID
    tagId: ID
    search: String
    limit: Int = 10
    offset: Int = 0
  ): [Post!]!
  post(id: ID!): Post

  # Comment Queries
  comments(postId: ID, limit: Int = 10, offset: Int = 0): [Comment!]!
  comment(id: ID!): Comment

  # Category Queries
  categories(limit: Int = 10, offset: Int = 0): [Category!]!
  category(id: ID!): Category

  # Tag Queries
  tags(limit: Int = 10, offset: Int = 0): [Tag!]!
  tag(id: ID!): Tag
}

# Root Mutation Type
type Mutation {
  # User Mutations
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  deleteUser(id: ID!): Boolean! # Returns true if successful

  # Post Mutations
  createPost(input: CreatePostInput!): Post!
  updatePost(id: ID!, input: UpdatePostInput!): Post!
  deletePost(id: ID!): Boolean!
  publishPost(id: ID!, published: Boolean!): Post!

  # Comment Mutations
  createComment(input: CreateCommentInput!): Comment! # Author ID derived from context/authentication
  updateComment(id: ID!, input: UpdateCommentInput!): Comment!
  deleteComment(id: ID!): Boolean!

  # Category Mutations
  createCategory(input: CreateCategoryInput!): Category!
  updateCategory(id: ID!, input: UpdateCategoryInput!): Category!
  deleteCategory(id: ID!): Boolean!

  # Tag Mutations
  createTag(input: CreateTagInput!): Tag!
  updateTag(id: ID!, input: UpdateTagInput!): Tag!
  deleteTag(id: ID!): Boolean!
}

# Root Subscription Type
type Subscription {
  # Post Subscriptions
  postAdded: Post! # Notifies when a new post is created
  postUpdated(id: ID!): Post! # Notifies when a specific post is updated

  # Comment Subscriptions
  commentAdded(postId: ID!): Comment! # Notifies when a comment is added to a specific post
}

4. Resolver Structure and Logic

Resolvers are functions that tell the GraphQL server how to fetch the data for a specific field in the schema. Each field in your schema (e.g., User.username, Query.posts, Mutation.createPost) needs a corresponding resolver function.

4.1. Resolver Signature

All resolvers follow the same signature: (parent, args, context, info) => data.

  • parent: The result of the parent resolver. Useful for nested fields (e.g., Post.author resolver receives the Post object as parent).
  • args: An object containing all arguments passed to the field (e.g., id for Query.post(id: ID!)).
  • context: An object shared across all resolvers in a single request. This is ideal for passing authenticated user information, database connections, or data loaders.
  • info: Contains information about the execution state of the query, including the schema, operation name,
graphql_schema_designer.txt
Download source file
Copy all content
Full output as text
Download ZIP
IDE-ready project ZIP
Copy share link
Permanent URL for this run
Get Embed Code
Embed this result on any website
Print / Save PDF
Use browser print dialog
\n\n\n"); var hasSrcMain=Object.keys(extracted).some(function(k){return k.indexOf("src/main")>=0;}); if(!hasSrcMain) zip.file(folder+"src/main."+ext,"import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport App from './App'\nimport './index.css'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n \n \n \n)\n"); var hasSrcApp=Object.keys(extracted).some(function(k){return k==="src/App."+ext||k==="App."+ext;}); if(!hasSrcApp) zip.file(folder+"src/App."+ext,"import React from 'react'\nimport './App.css'\n\nfunction App(){\n return(\n
\n
\n

"+slugTitle(pn)+"

\n

Built with PantheraHive BOS

\n
\n
\n )\n}\nexport default App\n"); zip.file(folder+"src/index.css","*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:#f0f2f5;color:#1a1a2e}\n.app{min-height:100vh;display:flex;flex-direction:column}\n.app-header{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:40px}\nh1{font-size:2.5rem;font-weight:700}\n"); zip.file(folder+"src/App.css",""); zip.file(folder+"src/components/.gitkeep",""); zip.file(folder+"src/pages/.gitkeep",""); zip.file(folder+"src/hooks/.gitkeep",""); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\n## Build\n\`\`\`bash\nnpm run build\n\`\`\`\n\n## Open in IDE\nOpen the project folder in VS Code or WebStorm.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n"); } /* --- Vue (Vite + Composition API + TypeScript) --- */ function buildVue(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var C=cc(pn); var extracted=extractCode(panelTxt); zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "0.0.0",\n "type": "module",\n "scripts": {\n "dev": "vite",\n "build": "vue-tsc -b && vite build",\n "preview": "vite preview"\n },\n "dependencies": {\n "vue": "^3.5.13",\n "vue-router": "^4.4.5",\n "pinia": "^2.3.0",\n "axios": "^1.7.9"\n },\n "devDependencies": {\n "@vitejs/plugin-vue": "^5.2.1",\n "typescript": "~5.7.3",\n "vite": "^6.0.5",\n "vue-tsc": "^2.2.0"\n }\n}\n'); zip.file(folder+"vite.config.ts","import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport { resolve } from 'path'\n\nexport default defineConfig({\n plugins: [vue()],\n resolve: { alias: { '@': resolve(__dirname,'src') } }\n})\n"); zip.file(folder+"tsconfig.json",'{"files":[],"references":[{"path":"./tsconfig.app.json"},{"path":"./tsconfig.node.json"}]}\n'); zip.file(folder+"tsconfig.app.json",'{\n "compilerOptions":{\n "target":"ES2020","useDefineForClassFields":true,"module":"ESNext","lib":["ES2020","DOM","DOM.Iterable"],\n "skipLibCheck":true,"moduleResolution":"bundler","allowImportingTsExtensions":true,\n "isolatedModules":true,"moduleDetection":"force","noEmit":true,"jsxImportSource":"vue",\n "strict":true,"paths":{"@/*":["./src/*"]}\n },\n "include":["src/**/*.ts","src/**/*.d.ts","src/**/*.tsx","src/**/*.vue"]\n}\n'); zip.file(folder+"env.d.ts","/// \n"); zip.file(folder+"index.html","\n\n\n \n \n "+slugTitle(pn)+"\n\n\n
\n \n\n\n"); var hasMain=Object.keys(extracted).some(function(k){return k==="src/main.ts"||k==="main.ts";}); if(!hasMain) zip.file(folder+"src/main.ts","import { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport App from './App.vue'\nimport './assets/main.css'\n\nconst app = createApp(App)\napp.use(createPinia())\napp.mount('#app')\n"); var hasApp=Object.keys(extracted).some(function(k){return k.indexOf("App.vue")>=0;}); if(!hasApp) zip.file(folder+"src/App.vue","\n\n\n\n\n"); zip.file(folder+"src/assets/main.css","*{margin:0;padding:0;box-sizing:border-box}body{font-family:system-ui,sans-serif;background:#fff;color:#213547}\n"); zip.file(folder+"src/components/.gitkeep",""); zip.file(folder+"src/views/.gitkeep",""); zip.file(folder+"src/stores/.gitkeep",""); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\n## Build\n\`\`\`bash\nnpm run build\n\`\`\`\n\nOpen in VS Code or WebStorm.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n"); } /* --- Angular (v19 standalone) --- */ function buildAngular(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var C=cc(pn); var sel=pn.replace(/_/g,"-"); var extracted=extractCode(panelTxt); zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "0.0.0",\n "scripts": {\n "ng": "ng",\n "start": "ng serve",\n "build": "ng build",\n "test": "ng test"\n },\n "dependencies": {\n "@angular/animations": "^19.0.0",\n "@angular/common": "^19.0.0",\n "@angular/compiler": "^19.0.0",\n "@angular/core": "^19.0.0",\n "@angular/forms": "^19.0.0",\n "@angular/platform-browser": "^19.0.0",\n "@angular/platform-browser-dynamic": "^19.0.0",\n "@angular/router": "^19.0.0",\n "rxjs": "~7.8.0",\n "tslib": "^2.3.0",\n "zone.js": "~0.15.0"\n },\n "devDependencies": {\n "@angular-devkit/build-angular": "^19.0.0",\n "@angular/cli": "^19.0.0",\n "@angular/compiler-cli": "^19.0.0",\n "typescript": "~5.6.0"\n }\n}\n'); zip.file(folder+"angular.json",'{\n "$schema": "./node_modules/@angular/cli/lib/config/schema.json",\n "version": 1,\n "newProjectRoot": "projects",\n "projects": {\n "'+pn+'": {\n "projectType": "application",\n "root": "",\n "sourceRoot": "src",\n "prefix": "app",\n "architect": {\n "build": {\n "builder": "@angular-devkit/build-angular:application",\n "options": {\n "outputPath": "dist/'+pn+'",\n "index": "src/index.html",\n "browser": "src/main.ts",\n "tsConfig": "tsconfig.app.json",\n "styles": ["src/styles.css"],\n "scripts": []\n }\n },\n "serve": {"builder":"@angular-devkit/build-angular:dev-server","configurations":{"production":{"buildTarget":"'+pn+':build:production"},"development":{"buildTarget":"'+pn+':build:development"}},"defaultConfiguration":"development"}\n }\n }\n }\n}\n'); zip.file(folder+"tsconfig.json",'{\n "compileOnSave": false,\n "compilerOptions": {"baseUrl":"./","outDir":"./dist/out-tsc","forceConsistentCasingInFileNames":true,"strict":true,"noImplicitOverride":true,"noPropertyAccessFromIndexSignature":true,"noImplicitReturns":true,"noFallthroughCasesInSwitch":true,"paths":{"@/*":["src/*"]},"skipLibCheck":true,"esModuleInterop":true,"sourceMap":true,"declaration":false,"experimentalDecorators":true,"moduleResolution":"bundler","importHelpers":true,"target":"ES2022","module":"ES2022","useDefineForClassFields":false,"lib":["ES2022","dom"]},\n "references":[{"path":"./tsconfig.app.json"}]\n}\n'); zip.file(folder+"tsconfig.app.json",'{\n "extends":"./tsconfig.json",\n "compilerOptions":{"outDir":"./dist/out-tsc","types":[]},\n "files":["src/main.ts"],\n "include":["src/**/*.d.ts"]\n}\n'); zip.file(folder+"src/index.html","\n\n\n \n "+slugTitle(pn)+"\n \n \n \n\n\n \n\n\n"); zip.file(folder+"src/main.ts","import { bootstrapApplication } from '@angular/platform-browser';\nimport { appConfig } from './app/app.config';\nimport { AppComponent } from './app/app.component';\n\nbootstrapApplication(AppComponent, appConfig)\n .catch(err => console.error(err));\n"); zip.file(folder+"src/styles.css","* { margin: 0; padding: 0; box-sizing: border-box; }\nbody { font-family: system-ui, -apple-system, sans-serif; background: #f9fafb; color: #111827; }\n"); var hasComp=Object.keys(extracted).some(function(k){return k.indexOf("app.component")>=0;}); if(!hasComp){ zip.file(folder+"src/app/app.component.ts","import { Component } from '@angular/core';\nimport { RouterOutlet } from '@angular/router';\n\n@Component({\n selector: 'app-root',\n standalone: true,\n imports: [RouterOutlet],\n templateUrl: './app.component.html',\n styleUrl: './app.component.css'\n})\nexport class AppComponent {\n title = '"+pn+"';\n}\n"); zip.file(folder+"src/app/app.component.html","
\n
\n

"+slugTitle(pn)+"

\n

Built with PantheraHive BOS

\n
\n \n
\n"); zip.file(folder+"src/app/app.component.css",".app-header{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:60vh;gap:16px}h1{font-size:2.5rem;font-weight:700;color:#6366f1}\n"); } zip.file(folder+"src/app/app.config.ts","import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';\nimport { provideRouter } from '@angular/router';\nimport { routes } from './app.routes';\n\nexport const appConfig: ApplicationConfig = {\n providers: [\n provideZoneChangeDetection({ eventCoalescing: true }),\n provideRouter(routes)\n ]\n};\n"); zip.file(folder+"src/app/app.routes.ts","import { Routes } from '@angular/router';\n\nexport const routes: Routes = [];\n"); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nng serve\n# or: npm start\n\`\`\`\n\n## Build\n\`\`\`bash\nng build\n\`\`\`\n\nOpen in VS Code with Angular Language Service extension.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n.angular/\n"); } /* --- Python --- */ function buildPython(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^\`\`\`[\w]*\n?/m,"").replace(/\n?\`\`\`$/m,"").trim(); var reqMap={"numpy":"numpy","pandas":"pandas","sklearn":"scikit-learn","tensorflow":"tensorflow","torch":"torch","flask":"flask","fastapi":"fastapi","uvicorn":"uvicorn","requests":"requests","sqlalchemy":"sqlalchemy","pydantic":"pydantic","dotenv":"python-dotenv","PIL":"Pillow","cv2":"opencv-python","matplotlib":"matplotlib","seaborn":"seaborn","scipy":"scipy"}; var reqs=[]; Object.keys(reqMap).forEach(function(k){if(src.indexOf("import "+k)>=0||src.indexOf("from "+k)>=0)reqs.push(reqMap[k]);}); var reqsTxt=reqs.length?reqs.join("\n"):"# add dependencies here\n"; zip.file(folder+"main.py",src||"# "+title+"\n# Generated by PantheraHive BOS\n\nprint(title+\" loaded\")\n"); zip.file(folder+"requirements.txt",reqsTxt); zip.file(folder+".env.example","# Environment variables\n"); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r requirements.txt\n\`\`\`\n\n## Run\n\`\`\`bash\npython main.py\n\`\`\`\n"); zip.file(folder+".gitignore",".venv/\n__pycache__/\n*.pyc\n.env\n.DS_Store\n"); } /* --- Node.js --- */ function buildNode(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^\`\`\`[\w]*\n?/m,"").replace(/\n?\`\`\`$/m,"").trim(); var depMap={"mongoose":"^8.0.0","dotenv":"^16.4.5","axios":"^1.7.9","cors":"^2.8.5","bcryptjs":"^2.4.3","jsonwebtoken":"^9.0.2","socket.io":"^4.7.4","uuid":"^9.0.1","zod":"^3.22.4","express":"^4.18.2"}; var deps={}; Object.keys(depMap).forEach(function(k){if(src.indexOf(k)>=0)deps[k]=depMap[k];}); if(!deps["express"])deps["express"]="^4.18.2"; var pkgJson=JSON.stringify({"name":pn,"version":"1.0.0","main":"src/index.js","scripts":{"start":"node src/index.js","dev":"nodemon src/index.js"},"dependencies":deps,"devDependencies":{"nodemon":"^3.0.3"}},null,2)+"\n"; zip.file(folder+"package.json",pkgJson); var fallback="const express=require(\"express\");\nconst app=express();\napp.use(express.json());\n\napp.get(\"/\",(req,res)=>{\n res.json({message:\""+title+" API\"});\n});\n\nconst PORT=process.env.PORT||3000;\napp.listen(PORT,()=>console.log(\"Server on port \"+PORT));\n"; zip.file(folder+"src/index.js",src||fallback); zip.file(folder+".env.example","PORT=3000\n"); zip.file(folder+".gitignore","node_modules/\n.env\n.DS_Store\n"); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\n\`\`\`\n\n## Run\n\`\`\`bash\nnpm run dev\n\`\`\`\n"); } /* --- Vanilla HTML --- */ function buildVanillaHtml(zip,folder,app,code){ var title=slugTitle(app); var isFullDoc=code.trim().toLowerCase().indexOf("=0||code.trim().toLowerCase().indexOf("=0; var indexHtml=isFullDoc?code:"\n\n\n\n\n"+title+"\n\n\n\n"+code+"\n\n\n\n"; zip.file(folder+"index.html",indexHtml); zip.file(folder+"style.css","/* "+title+" — styles */\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:#fff;color:#1a1a2e}\n"); zip.file(folder+"script.js","/* "+title+" — scripts */\n"); zip.file(folder+"assets/.gitkeep",""); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Open\nDouble-click \`index.html\` in your browser.\n\nOr serve locally:\n\`\`\`bash\nnpx serve .\n# or\npython3 -m http.server 3000\n\`\`\`\n"); zip.file(folder+".gitignore",".DS_Store\nnode_modules/\n.env\n"); } /* ===== MAIN ===== */ var sc=document.createElement("script"); sc.src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"; sc.onerror=function(){ if(lbl)lbl.textContent="Download ZIP"; alert("JSZip load failed — check connection."); }; sc.onload=function(){ var zip=new JSZip(); var base=(_phFname||"output").replace(/\.[^.]+$/,""); var app=base.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"my_app"; var folder=app+"/"; var vc=document.getElementById("panel-content"); var panelTxt=vc?(vc.innerText||vc.textContent||""):""; var lang=detectLang(_phCode,panelTxt); if(_phIsHtml){ buildVanillaHtml(zip,folder,app,_phCode); } else if(lang==="flutter"){ buildFlutter(zip,folder,app,_phCode,panelTxt); } else if(lang==="react-native"){ buildReactNative(zip,folder,app,_phCode,panelTxt); } else if(lang==="swift"){ buildSwift(zip,folder,app,_phCode,panelTxt); } else if(lang==="kotlin"){ buildKotlin(zip,folder,app,_phCode,panelTxt); } else if(lang==="react"){ buildReact(zip,folder,app,_phCode,panelTxt); } else if(lang==="vue"){ buildVue(zip,folder,app,_phCode,panelTxt); } else if(lang==="angular"){ buildAngular(zip,folder,app,_phCode,panelTxt); } else if(lang==="python"){ buildPython(zip,folder,app,_phCode); } else if(lang==="node"){ buildNode(zip,folder,app,_phCode); } else { /* Document/content workflow */ var title=app.replace(/_/g," "); var md=_phAll||_phCode||panelTxt||"No content"; zip.file(folder+app+".md",md); var h=""+title+""; h+="

"+title+"

"; var hc=md.replace(/&/g,"&").replace(//g,">"); hc=hc.replace(/^### (.+)$/gm,"

$1

"); hc=hc.replace(/^## (.+)$/gm,"

$1

"); hc=hc.replace(/^# (.+)$/gm,"

$1

"); hc=hc.replace(/\*\*(.+?)\*\*/g,"$1"); hc=hc.replace(/\n{2,}/g,"

"); h+="

"+hc+"

Generated by PantheraHive BOS
"; zip.file(folder+app+".html",h); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\nFiles:\n- "+app+".md (Markdown)\n- "+app+".html (styled HTML)\n"); } zip.generateAsync({type:"blob"}).then(function(blob){ var a=document.createElement("a"); a.href=URL.createObjectURL(blob); a.download=app+".zip"; a.click(); URL.revokeObjectURL(a.href); if(lbl)lbl.textContent="Download ZIP"; }); }; document.head.appendChild(sc); } function phShare(){navigator.clipboard.writeText(window.location.href).then(function(){var el=document.getElementById("ph-share-lbl");if(el){el.textContent="Link copied!";setTimeout(function(){el.textContent="Copy share link";},2500);}});}function phEmbed(){var runId=window.location.pathname.split("/").pop().replace(".html","");var embedUrl="https://pantherahive.com/embed/"+runId;var code='';navigator.clipboard.writeText(code).then(function(){var el=document.getElementById("ph-embed-lbl");if(el){el.textContent="Embed code copied!";setTimeout(function(){el.textContent="Get Embed Code";},2500);}});}