From eee352d908dbef02acbfa380600e15d8563de9af Mon Sep 17 00:00:00 2001 From: Ryth Azhur Date: Tue, 31 Mar 2026 20:41:20 -0400 Subject: [PATCH] Move desktop and e2e into examples/ directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - desktop/ → examples/django-react-desktop-app/ - e2e/ → examples/django-react-site/ - example/ → examples/django-react-site/backend/ - Update Dockerfile.test, Makefile, playwright config, and django.config.mjs path references Co-Authored-By: Claude Opus 4.6 (1M context) --- Dockerfile.test | 2 +- Makefile | 2 +- README.md | 8 +- .../django-react-desktop-app}/app.py | 0 .../backend/__init__.py | 0 .../django-react-desktop-app}/backend/apps.py | 0 .../django-react-desktop-app}/backend/asgi.py | 0 .../backend/djarea_clients.py | 0 .../backend/models.py | 0 .../backend/settings.py | 0 .../django-react-desktop-app}/backend/urls.py | 0 .../frontend/index.html | 0 .../frontend/package.json | 0 .../frontend/src/App.tsx | 0 .../frontend/src/main.tsx | 0 .../frontend/tsconfig.json | 0 .../frontend/vite.config.ts | 0 .../django-react-desktop-app}/manage.py | 0 .../django-react-desktop-app}/pyproject.toml | 0 .../tests/__init__.py | 0 .../tests/conftest.py | 0 .../tests/test_desktop_rpc.py | 0 .../tests/test_notes.py | 0 .../tests/test_system.py | 0 .../django-react-site/backend}/manage.py | 0 .../backend}/testapp/__init__.py | 0 .../backend}/testapp/apps.py | 0 .../backend}/testapp/asgi.py | 0 .../backend}/testapp/djarea_clients.py | 0 .../backend}/testapp/models.py | 0 .../backend}/testapp/settings.py | 0 .../backend}/testapp/urls.py | 0 .../django-react-site}/djarea.spec.ts | 0 .../harness/django.config.mjs | 6 +- .../django-react-site}/harness/index.html | 0 .../django-react-site}/harness/package.json | 0 .../src/api/generated.channels.hooks.tsx | 40 + .../src/api/generated.channels.schema.json | 268 ++ .../harness/src/api/generated.channels.ts | 337 +++ .../src/api/generated.django.server.ts | 62 + .../harness/src/api/generated.django.tsx | 257 ++ .../src/api/generated.djarea.schema.json | 2656 +++++++++++++++++ .../harness/src/api/generated.djarea.ts | 2123 +++++++++++++ .../harness/src/api/generated.forms.ts | 226 ++ .../harness/src/api/index.ts | 0 .../harness/src/fixtures.tsx | 0 .../django-react-site}/harness/src/main.tsx | 0 .../harness/test-results/.last-run.json | 4 + .../django-react-site}/harness/tsconfig.json | 0 .../django-react-site}/harness/vite.config.ts | 0 playwright.config.ts | 2 +- 51 files changed, 5983 insertions(+), 10 deletions(-) rename {desktop => examples/django-react-desktop-app}/app.py (100%) rename {desktop => examples/django-react-desktop-app}/backend/__init__.py (100%) rename {desktop => examples/django-react-desktop-app}/backend/apps.py (100%) rename {desktop => examples/django-react-desktop-app}/backend/asgi.py (100%) rename {desktop => examples/django-react-desktop-app}/backend/djarea_clients.py (100%) rename {desktop => examples/django-react-desktop-app}/backend/models.py (100%) rename {desktop => examples/django-react-desktop-app}/backend/settings.py (100%) rename {desktop => examples/django-react-desktop-app}/backend/urls.py (100%) rename {desktop => examples/django-react-desktop-app}/frontend/index.html (100%) rename {desktop => examples/django-react-desktop-app}/frontend/package.json (100%) rename {desktop => examples/django-react-desktop-app}/frontend/src/App.tsx (100%) rename {desktop => examples/django-react-desktop-app}/frontend/src/main.tsx (100%) rename {desktop => examples/django-react-desktop-app}/frontend/tsconfig.json (100%) rename {desktop => examples/django-react-desktop-app}/frontend/vite.config.ts (100%) rename {desktop => examples/django-react-desktop-app}/manage.py (100%) rename {desktop => examples/django-react-desktop-app}/pyproject.toml (100%) rename {desktop => examples/django-react-desktop-app}/tests/__init__.py (100%) rename {desktop => examples/django-react-desktop-app}/tests/conftest.py (100%) rename {desktop => examples/django-react-desktop-app}/tests/test_desktop_rpc.py (100%) rename {desktop => examples/django-react-desktop-app}/tests/test_notes.py (100%) rename {desktop => examples/django-react-desktop-app}/tests/test_system.py (100%) rename {example => examples/django-react-site/backend}/manage.py (100%) rename {example => examples/django-react-site/backend}/testapp/__init__.py (100%) rename {example => examples/django-react-site/backend}/testapp/apps.py (100%) rename {example => examples/django-react-site/backend}/testapp/asgi.py (100%) rename {example => examples/django-react-site/backend}/testapp/djarea_clients.py (100%) rename {example => examples/django-react-site/backend}/testapp/models.py (100%) rename {example => examples/django-react-site/backend}/testapp/settings.py (100%) rename {example => examples/django-react-site/backend}/testapp/urls.py (100%) rename {e2e => examples/django-react-site}/djarea.spec.ts (100%) rename {e2e => examples/django-react-site}/harness/django.config.mjs (71%) rename {e2e => examples/django-react-site}/harness/index.html (100%) rename {e2e => examples/django-react-site}/harness/package.json (100%) create mode 100644 examples/django-react-site/harness/src/api/generated.channels.hooks.tsx create mode 100644 examples/django-react-site/harness/src/api/generated.channels.schema.json create mode 100644 examples/django-react-site/harness/src/api/generated.channels.ts create mode 100644 examples/django-react-site/harness/src/api/generated.django.server.ts create mode 100644 examples/django-react-site/harness/src/api/generated.django.tsx create mode 100644 examples/django-react-site/harness/src/api/generated.djarea.schema.json create mode 100644 examples/django-react-site/harness/src/api/generated.djarea.ts create mode 100644 examples/django-react-site/harness/src/api/generated.forms.ts rename {e2e => examples/django-react-site}/harness/src/api/index.ts (100%) rename {e2e => examples/django-react-site}/harness/src/fixtures.tsx (100%) rename {e2e => examples/django-react-site}/harness/src/main.tsx (100%) create mode 100644 examples/django-react-site/harness/test-results/.last-run.json rename {e2e => examples/django-react-site}/harness/tsconfig.json (100%) rename {e2e => examples/django-react-site}/harness/vite.config.ts (100%) diff --git a/Dockerfile.test b/Dockerfile.test index 9c04acc..f08fe66 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -12,7 +12,7 @@ COPY django/ /app/django/ RUN pip install --no-cache-dir /app/django[channels] daphne # Copy example app -COPY example/ /app/example/ +COPY examples/django-react-site/backend/ /app/example/ WORKDIR /app/example diff --git a/Makefile b/Makefile index fae896d..8e79305 100644 --- a/Makefile +++ b/Makefile @@ -43,4 +43,4 @@ clean: docker compose -f docker-compose.test.yml down -v --remove-orphans 2>/dev/null || true rm -rf django/src/mizan.egg-info django/dist django/build rm -rf react/dist react/node_modules - rm -f example/db.sqlite3 + rm -f examples/django-react-site/backend/db.sqlite3 diff --git a/README.md b/README.md index 9df30b1..352ee00 100644 --- a/README.md +++ b/README.md @@ -276,7 +276,7 @@ cd react && npm test # E2E integration tests (real browser, real backend) docker compose -f docker-compose.test.yml up -d -cd e2e/harness && npm install && npx mizan-generate && npx vite --port 5174 & +cd examples/django-react-site/harness && npm install && npx mizan-generate && npx vite --port 5174 & npx playwright test # All at once @@ -289,8 +289,8 @@ make test-all mizan/ django/ Python package (mizan) react/ TypeScript package (@rythazhur/mizan) - example/ Integration test backend (Docker) - desktop/ PyWebView desktop test app - e2e/ Playwright E2E tests + React harness + examples/ + django-react-site/ E2E tests, React harness, Django backend + django-react-desktop-app/ PyWebView desktop app Makefile Test orchestration ``` diff --git a/desktop/app.py b/examples/django-react-desktop-app/app.py similarity index 100% rename from desktop/app.py rename to examples/django-react-desktop-app/app.py diff --git a/desktop/backend/__init__.py b/examples/django-react-desktop-app/backend/__init__.py similarity index 100% rename from desktop/backend/__init__.py rename to examples/django-react-desktop-app/backend/__init__.py diff --git a/desktop/backend/apps.py b/examples/django-react-desktop-app/backend/apps.py similarity index 100% rename from desktop/backend/apps.py rename to examples/django-react-desktop-app/backend/apps.py diff --git a/desktop/backend/asgi.py b/examples/django-react-desktop-app/backend/asgi.py similarity index 100% rename from desktop/backend/asgi.py rename to examples/django-react-desktop-app/backend/asgi.py diff --git a/desktop/backend/djarea_clients.py b/examples/django-react-desktop-app/backend/djarea_clients.py similarity index 100% rename from desktop/backend/djarea_clients.py rename to examples/django-react-desktop-app/backend/djarea_clients.py diff --git a/desktop/backend/models.py b/examples/django-react-desktop-app/backend/models.py similarity index 100% rename from desktop/backend/models.py rename to examples/django-react-desktop-app/backend/models.py diff --git a/desktop/backend/settings.py b/examples/django-react-desktop-app/backend/settings.py similarity index 100% rename from desktop/backend/settings.py rename to examples/django-react-desktop-app/backend/settings.py diff --git a/desktop/backend/urls.py b/examples/django-react-desktop-app/backend/urls.py similarity index 100% rename from desktop/backend/urls.py rename to examples/django-react-desktop-app/backend/urls.py diff --git a/desktop/frontend/index.html b/examples/django-react-desktop-app/frontend/index.html similarity index 100% rename from desktop/frontend/index.html rename to examples/django-react-desktop-app/frontend/index.html diff --git a/desktop/frontend/package.json b/examples/django-react-desktop-app/frontend/package.json similarity index 100% rename from desktop/frontend/package.json rename to examples/django-react-desktop-app/frontend/package.json diff --git a/desktop/frontend/src/App.tsx b/examples/django-react-desktop-app/frontend/src/App.tsx similarity index 100% rename from desktop/frontend/src/App.tsx rename to examples/django-react-desktop-app/frontend/src/App.tsx diff --git a/desktop/frontend/src/main.tsx b/examples/django-react-desktop-app/frontend/src/main.tsx similarity index 100% rename from desktop/frontend/src/main.tsx rename to examples/django-react-desktop-app/frontend/src/main.tsx diff --git a/desktop/frontend/tsconfig.json b/examples/django-react-desktop-app/frontend/tsconfig.json similarity index 100% rename from desktop/frontend/tsconfig.json rename to examples/django-react-desktop-app/frontend/tsconfig.json diff --git a/desktop/frontend/vite.config.ts b/examples/django-react-desktop-app/frontend/vite.config.ts similarity index 100% rename from desktop/frontend/vite.config.ts rename to examples/django-react-desktop-app/frontend/vite.config.ts diff --git a/desktop/manage.py b/examples/django-react-desktop-app/manage.py similarity index 100% rename from desktop/manage.py rename to examples/django-react-desktop-app/manage.py diff --git a/desktop/pyproject.toml b/examples/django-react-desktop-app/pyproject.toml similarity index 100% rename from desktop/pyproject.toml rename to examples/django-react-desktop-app/pyproject.toml diff --git a/desktop/tests/__init__.py b/examples/django-react-desktop-app/tests/__init__.py similarity index 100% rename from desktop/tests/__init__.py rename to examples/django-react-desktop-app/tests/__init__.py diff --git a/desktop/tests/conftest.py b/examples/django-react-desktop-app/tests/conftest.py similarity index 100% rename from desktop/tests/conftest.py rename to examples/django-react-desktop-app/tests/conftest.py diff --git a/desktop/tests/test_desktop_rpc.py b/examples/django-react-desktop-app/tests/test_desktop_rpc.py similarity index 100% rename from desktop/tests/test_desktop_rpc.py rename to examples/django-react-desktop-app/tests/test_desktop_rpc.py diff --git a/desktop/tests/test_notes.py b/examples/django-react-desktop-app/tests/test_notes.py similarity index 100% rename from desktop/tests/test_notes.py rename to examples/django-react-desktop-app/tests/test_notes.py diff --git a/desktop/tests/test_system.py b/examples/django-react-desktop-app/tests/test_system.py similarity index 100% rename from desktop/tests/test_system.py rename to examples/django-react-desktop-app/tests/test_system.py diff --git a/example/manage.py b/examples/django-react-site/backend/manage.py similarity index 100% rename from example/manage.py rename to examples/django-react-site/backend/manage.py diff --git a/example/testapp/__init__.py b/examples/django-react-site/backend/testapp/__init__.py similarity index 100% rename from example/testapp/__init__.py rename to examples/django-react-site/backend/testapp/__init__.py diff --git a/example/testapp/apps.py b/examples/django-react-site/backend/testapp/apps.py similarity index 100% rename from example/testapp/apps.py rename to examples/django-react-site/backend/testapp/apps.py diff --git a/example/testapp/asgi.py b/examples/django-react-site/backend/testapp/asgi.py similarity index 100% rename from example/testapp/asgi.py rename to examples/django-react-site/backend/testapp/asgi.py diff --git a/example/testapp/djarea_clients.py b/examples/django-react-site/backend/testapp/djarea_clients.py similarity index 100% rename from example/testapp/djarea_clients.py rename to examples/django-react-site/backend/testapp/djarea_clients.py diff --git a/example/testapp/models.py b/examples/django-react-site/backend/testapp/models.py similarity index 100% rename from example/testapp/models.py rename to examples/django-react-site/backend/testapp/models.py diff --git a/example/testapp/settings.py b/examples/django-react-site/backend/testapp/settings.py similarity index 100% rename from example/testapp/settings.py rename to examples/django-react-site/backend/testapp/settings.py diff --git a/example/testapp/urls.py b/examples/django-react-site/backend/testapp/urls.py similarity index 100% rename from example/testapp/urls.py rename to examples/django-react-site/backend/testapp/urls.py diff --git a/e2e/djarea.spec.ts b/examples/django-react-site/djarea.spec.ts similarity index 100% rename from e2e/djarea.spec.ts rename to examples/django-react-site/djarea.spec.ts diff --git a/e2e/harness/django.config.mjs b/examples/django-react-site/harness/django.config.mjs similarity index 71% rename from e2e/harness/django.config.mjs rename to examples/django-react-site/harness/django.config.mjs index 51f627d..0693dd8 100644 --- a/e2e/harness/django.config.mjs +++ b/examples/django-react-site/harness/django.config.mjs @@ -2,17 +2,17 @@ import path from 'path' import { fileURLToPath } from 'url' const __dirname = path.dirname(fileURLToPath(import.meta.url)) -const root = path.resolve(__dirname, '../..') +const root = path.resolve(__dirname, '../../..') export default { projectId: 'e2e-harness', source: { django: { - managePath: path.join(root, 'example/manage.py'), + managePath: path.join(root, 'examples/django-react-site/backend/manage.py'), command: [path.join(root, 'django/.venv/bin/python')], env: { - PYTHONPATH: `${path.join(root, 'django/src')}:${path.join(root, 'example')}`, + PYTHONPATH: `${path.join(root, 'django/src')}:${path.join(root, 'examples/django-react-site/backend')}`, DJANGO_SETTINGS_MODULE: 'testapp.settings', }, }, diff --git a/e2e/harness/index.html b/examples/django-react-site/harness/index.html similarity index 100% rename from e2e/harness/index.html rename to examples/django-react-site/harness/index.html diff --git a/e2e/harness/package.json b/examples/django-react-site/harness/package.json similarity index 100% rename from e2e/harness/package.json rename to examples/django-react-site/harness/package.json diff --git a/examples/django-react-site/harness/src/api/generated.channels.hooks.tsx b/examples/django-react-site/harness/src/api/generated.channels.hooks.tsx new file mode 100644 index 0000000..53e5679 --- /dev/null +++ b/examples/django-react-site/harness/src/api/generated.channels.hooks.tsx @@ -0,0 +1,40 @@ +'use client' + +// AUTO-GENERATED by mizan - do not edit manually +// Regenerate with: npm run schemas + +import { useChannel, type ChannelSubscription } from 'mizan/channels' + +import type { ChatParams, ChatReactMessage, ChatDjangoMessage, NotificationsDjangoMessage, PresenceDjangoMessage, PrivateDjangoMessage } from './generated.channels' + +// ============================================================================ +// Channel Hooks +// ============================================================================ + +/** + * Hook for the chat channel. + */ +export function useChatChannel(params: ChatParams): ChannelSubscription { + return useChannel('chat', params) +} + +/** + * Hook for the notifications channel. + */ +export function useNotificationsChannel(): ChannelSubscription, NotificationsDjangoMessage, never> { + return useChannel('notifications', {}) +} + +/** + * Hook for the presence channel. + */ +export function usePresenceChannel(): ChannelSubscription, PresenceDjangoMessage, never> { + return useChannel('presence', {}) +} + +/** + * Hook for the private channel. + */ +export function usePrivateChannel(): ChannelSubscription, PrivateDjangoMessage, never> { + return useChannel('private', {}) +} diff --git a/examples/django-react-site/harness/src/api/generated.channels.schema.json b/examples/django-react-site/harness/src/api/generated.channels.schema.json new file mode 100644 index 0000000..b7e0acd --- /dev/null +++ b/examples/django-react-site/harness/src/api/generated.channels.schema.json @@ -0,0 +1,268 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "mizan Channels", + "version": "1.0.0", + "description": "Auto-generated schema for mizan channels" + }, + "paths": { + "/channels/chat/params": { + "post": { + "operationId": "chatParams", + "summary": "Chat channel params", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BaseModel" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatParams" + } + } + }, + "required": true + } + } + }, + "/channels/chat/react": { + "post": { + "operationId": "chatReactMessage", + "summary": "Chat React→Django message", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BaseModel" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatReactMessage" + } + } + }, + "required": true + } + } + }, + "/channels/chat/django": { + "post": { + "operationId": "chatDjangoMessage", + "summary": "Chat Django→React message", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatDjangoMessage" + } + } + } + } + } + } + }, + "/channels/notifications/django": { + "post": { + "operationId": "notificationsDjangoMessage", + "summary": "Notifications Django→React message", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationsDjangoMessage" + } + } + } + } + } + } + }, + "/channels/presence/django": { + "post": { + "operationId": "presenceDjangoMessage", + "summary": "Presence Django→React message", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PresenceDjangoMessage" + } + } + } + } + } + } + }, + "/channels/private/django": { + "post": { + "operationId": "privateDjangoMessage", + "summary": "Private Django→React message", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PrivateDjangoMessage" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "BaseModel": { + "properties": {}, + "title": "BaseModel", + "type": "object" + }, + "ChatParams": { + "properties": { + "room": { + "title": "Room", + "type": "string" + } + }, + "required": [ + "room" + ], + "title": "ChatParams", + "type": "object" + }, + "ChatReactMessage": { + "properties": { + "text": { + "title": "Text", + "type": "string" + } + }, + "required": [ + "text" + ], + "title": "ChatReactMessage", + "type": "object" + }, + "ChatDjangoMessage": { + "properties": { + "text": { + "title": "Text", + "type": "string" + } + }, + "required": [ + "text" + ], + "title": "ChatDjangoMessage", + "type": "object" + }, + "NotificationsDjangoMessage": { + "properties": { + "text": { + "title": "Text", + "type": "string" + } + }, + "required": [ + "text" + ], + "title": "NotificationsDjangoMessage", + "type": "object" + }, + "PresenceDjangoMessage": { + "properties": { + "value": { + "title": "Value", + "type": "integer" + } + }, + "required": [ + "value" + ], + "title": "PresenceDjangoMessage", + "type": "object" + }, + "PrivateDjangoMessage": { + "properties": { + "text": { + "title": "Text", + "type": "string" + } + }, + "required": [ + "text" + ], + "title": "PrivateDjangoMessage", + "type": "object" + } + } + }, + "servers": [], + "x-mizan-channels": [ + { + "name": "chat", + "pascalName": "Chat", + "hasParams": true, + "hasReactMessage": true, + "hasDjangoMessage": true, + "paramsType": "ChatParams", + "reactMessageType": "ChatReactMessage", + "djangoMessageType": "ChatDjangoMessage" + }, + { + "name": "notifications", + "pascalName": "Notifications", + "hasParams": false, + "hasReactMessage": false, + "hasDjangoMessage": true, + "djangoMessageType": "NotificationsDjangoMessage" + }, + { + "name": "presence", + "pascalName": "Presence", + "hasParams": false, + "hasReactMessage": false, + "hasDjangoMessage": true, + "djangoMessageType": "PresenceDjangoMessage" + }, + { + "name": "private", + "pascalName": "Private", + "hasParams": false, + "hasReactMessage": false, + "hasDjangoMessage": true, + "djangoMessageType": "PrivateDjangoMessage" + } + ] +} \ No newline at end of file diff --git a/examples/django-react-site/harness/src/api/generated.channels.ts b/examples/django-react-site/harness/src/api/generated.channels.ts new file mode 100644 index 0000000..23ae760 --- /dev/null +++ b/examples/django-react-site/harness/src/api/generated.channels.ts @@ -0,0 +1,337 @@ +// AUTO-GENERATED by mizan - do not edit manually +// Regenerate with: npm run schemas + +// ============================================================================ +// OpenAPI Types (generated by openapi-typescript) +// ============================================================================ + +export interface paths { + "/channels/chat/params": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Chat channel params */ + post: operations["chatParams"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/channels/chat/react": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Chat React→Django message */ + post: operations["chatReactMessage"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/channels/chat/django": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Chat Django→React message */ + post: operations["chatDjangoMessage"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/channels/notifications/django": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Notifications Django→React message */ + post: operations["notificationsDjangoMessage"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/channels/presence/django": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Presence Django→React message */ + post: operations["presenceDjangoMessage"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/channels/private/django": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Private Django→React message */ + post: operations["privateDjangoMessage"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + /** BaseModel */ + BaseModel: Record; + /** ChatParams */ + ChatParams: { + /** Room */ + room: string; + }; + /** ChatReactMessage */ + ChatReactMessage: { + /** Text */ + text: string; + }; + /** ChatDjangoMessage */ + ChatDjangoMessage: { + /** Text */ + text: string; + }; + /** NotificationsDjangoMessage */ + NotificationsDjangoMessage: { + /** Text */ + text: string; + }; + /** PresenceDjangoMessage */ + PresenceDjangoMessage: { + /** Value */ + value: number; + }; + /** PrivateDjangoMessage */ + PrivateDjangoMessage: { + /** Text */ + text: string; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + chatParams: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ChatParams"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BaseModel"]; + }; + }; + }; + }; + chatReactMessage: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ChatReactMessage"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BaseModel"]; + }; + }; + }; + }; + chatDjangoMessage: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ChatDjangoMessage"]; + }; + }; + }; + }; + notificationsDjangoMessage: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotificationsDjangoMessage"]; + }; + }; + }; + }; + presenceDjangoMessage: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PresenceDjangoMessage"]; + }; + }; + }; + }; + privateDjangoMessage: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PrivateDjangoMessage"]; + }; + }; + }; + }; +} + + +// ============================================================================ +// Convenience Type Exports +// ============================================================================ + +export type ChatParams = components["schemas"]["ChatParams"] +export type ChatReactMessage = components["schemas"]["ChatReactMessage"] +export type ChatDjangoMessage = components["schemas"]["ChatDjangoMessage"] +export type NotificationsDjangoMessage = components["schemas"]["NotificationsDjangoMessage"] +export type PresenceDjangoMessage = components["schemas"]["PresenceDjangoMessage"] +export type PrivateDjangoMessage = components["schemas"]["PrivateDjangoMessage"] + +// ============================================================================ +// Channel Registry +// ============================================================================ + +export const CHANNELS = { + chat: { + name: 'chat', + pascalName: 'Chat', + hasParams: true, + hasReactMessage: true, + hasDjangoMessage: true, + paramsType: 'ChatParams', + reactMessageType: 'ChatReactMessage', + djangoMessageType: 'ChatDjangoMessage', + }, + notifications: { + name: 'notifications', + pascalName: 'Notifications', + hasParams: false, + hasReactMessage: false, + hasDjangoMessage: true, + djangoMessageType: 'NotificationsDjangoMessage', + }, + presence: { + name: 'presence', + pascalName: 'Presence', + hasParams: false, + hasReactMessage: false, + hasDjangoMessage: true, + djangoMessageType: 'PresenceDjangoMessage', + }, + private: { + name: 'private', + pascalName: 'Private', + hasParams: false, + hasReactMessage: false, + hasDjangoMessage: true, + djangoMessageType: 'PrivateDjangoMessage', + }, +} as const diff --git a/examples/django-react-site/harness/src/api/generated.django.server.ts b/examples/django-react-site/harness/src/api/generated.django.server.ts new file mode 100644 index 0000000..4f54d02 --- /dev/null +++ b/examples/django-react-site/harness/src/api/generated.django.server.ts @@ -0,0 +1,62 @@ +// AUTO-GENERATED by mizan - do not edit manually +// Regenerate with: npm run schemas +// +// Server-side functions for SSR hydration. +// These run in Next.js server components/layouts. + +import type { currentUserOutput, greetOutput } from './generated.mizan' + +// ============================================================================ +// Hydration Types +// ============================================================================ + +/** Typed hydration data for SSR */ +export interface DjangoHydration { + currentUser?: currentUserOutput + greet?: greetOutput +} + +// ============================================================================ +// SSR Hydration Helper +// ============================================================================ + +/** + * Fetch hydration data for SSR. + * + * Call this in your server component: + * const hydration = await getDjangoHydration(client) + * return ... + */ +export async function getDjangoHydration( + client: { request: (method: string, url: string, body?: unknown) => Promise } +): Promise { + const hydration: DjangoHydration = {} + + const results = await Promise.allSettled([ + client.request('POST', '/api/mizan/call/', { fn: 'current_user', args: {} }), + client.request('POST', '/api/mizan/call/', { fn: 'greet', args: {} }), + ]) + + if (results[0].status === 'fulfilled') { + const data = await (results[0] as PromiseFulfilledResult).value.json() + if (data.error) { + console.error('[getDjangoHydration] current_user failed:', data.code, data.message) + } else { + hydration.currentUser = data.data + } + } else { + console.error('[getDjangoHydration] current_user request failed:', (results[0] as PromiseRejectedResult).reason) + } + if (results[1].status === 'fulfilled') { + const data = await (results[1] as PromiseFulfilledResult).value.json() + if (data.error) { + console.error('[getDjangoHydration] greet failed:', data.code, data.message) + } else { + hydration.greet = data.data + } + } else { + console.error('[getDjangoHydration] greet request failed:', (results[1] as PromiseRejectedResult).reason) + } + + return hydration +} diff --git a/examples/django-react-site/harness/src/api/generated.django.tsx b/examples/django-react-site/harness/src/api/generated.django.tsx new file mode 100644 index 0000000..d672dff --- /dev/null +++ b/examples/django-react-site/harness/src/api/generated.django.tsx @@ -0,0 +1,257 @@ +'use client' + +// AUTO-GENERATED by mizan - do not edit manually +// Regenerate with: npm run schemas + +// This file provides typed wrappers around the mizan library. +// - DjangoContext: Typed provider wrapping mizanProvider +// - Typed hooks: useAuthStatus(), useUser(), etc. + +import { type ReactNode, useCallback } from 'react' +import { + mizanProvider, + usemizan, + usemizanContext, + usemizanCall, + type mizanHydration, + type Transport, +} from 'mizan' +import { ChannelProvider, ChannelConnection } from 'mizan/channels' +import { useRef } from 'react' + +import type { addEmailSchemaOutput, addEmailValidateInput, addEmailValidateOutput, addInput, addOutput, buggyFnOutput, contactSchemaInput, contactSchemaOutput, contactSubmitOutput, contactValidateInput, contactValidateOutput, currentUserOutput, echoInput, echoOutput, greetInput, greetOutput, httpOnlyEchoInput, httpOnlyEchoOutput, itemFormsetSchemaInput, itemFormsetSchemaOutput, itemFormsetSubmitInput, itemFormsetSubmitOutput, itemFormsetValidateInput, itemFormsetValidateOutput, itemSchemaInput, itemSchemaOutput, itemSubmitOutput, itemValidateInput, itemValidateOutput, jwtObtainOutput, jwtRefreshInput, jwtRefreshOutput, loginSchemaOutput, loginSubmitInput, loginSubmitOutput, loginValidateInput, loginValidateOutput, multiplyInput, multiplyOutput, notImplementedFnOutput, permissionCheckFnInput, permissionCheckFnOutput, signupSchemaOutput, signupSubmitInput, signupSubmitOutput, signupValidateInput, signupValidateOutput, staffOnlyOutput, superuserOnlyOutput, verifiedOnlyOutput, whoamiOutput, wsWhoamiOutput } from './generated.mizan' + +// ============================================================================ +// Hydration Types +// ============================================================================ + +/** Typed hydration data for SSR */ +export interface DjangoHydration { + currentUser?: currentUserOutput + greet?: greetOutput +} + +/** Convert typed hydration to mizan format */ +function tomizanHydration(hydration?: DjangoHydration): mizanHydration | undefined { + if (!hydration) return undefined + const result: mizanHydration = {} + if (hydration.currentUser !== undefined) result['current_user'] = hydration.currentUser + if (hydration.greet !== undefined) result['greet'] = hydration.greet + return result +} + +// ============================================================================ +// Provider +// ============================================================================ + +export interface DjangoContextProps { + children: ReactNode + /** SSR hydration data */ + hydration?: DjangoHydration + /** WebSocket URL for RPC calls (default: /ws/) */ + wsUrl?: string + /** Base URL for HTTP fallback (default: /api/mizan) */ + baseUrl?: string +} + +/** + * Typed Django context provider. + * + * Wraps mizanProvider with: + * - Typed hydration + * - Auto-fetch for registered contexts + * + * Usage: + * + * + * + */ +export function DjangoContext({ + children, + hydration, + wsUrl, + baseUrl, +}: DjangoContextProps) { + const connectionRef = useRef(null) + if (!connectionRef.current) { + connectionRef.current = new ChannelConnection({ url: wsUrl || '/ws/' }) + } + + return ( + + + {children} + + + ) +} + +// ============================================================================ +// Context Hooks (typed wrappers) +// ============================================================================ + +/** + * Get current_user context data. + * @throws if context not loaded yet + */ +export function useCurrentUser(): currentUserOutput { + const data = usemizanContext('current_user') + if (data === undefined) { + throw new Error('useCurrentUser: context not loaded yet') + } + return data +} + +/** + * Get greet context data. + * @throws if context not loaded yet + */ +export function useGreet(): greetOutput { + const data = usemizanContext('greet') + if (data === undefined) { + throw new Error('useGreet: context not loaded yet') + } + return data +} + +/** + * Get context refresh functions without subscribing to data changes. + * Use this in components that only need to trigger refreshes. + */ +export function useDjangoRefresh() { + const { refreshContext, refreshAllContexts } = usemizan() + return { + refreshCurrentUser: () => refreshContext('current_user'), + refreshGreet: () => refreshContext('greet'), + refreshAll: refreshAllContexts, + } +} + +// ============================================================================ +// Function Hooks (typed wrappers) +// ============================================================================ + +/** + * Call echo server function. + * Transport: websocket + */ +export function useEcho() { + return usemizanCall('echo', 'websocket') +} + +/** + * Call add server function. + * Transport: websocket + */ +export function useAdd() { + return usemizanCall('add', 'websocket') +} + +/** + * Call whoami server function. + * Transport: http + */ +export function useWhoami() { + return usemizanCall('whoami', 'http') +} + +/** + * Call http_only_echo server function. + * Transport: http + */ +export function useHttpOnlyEcho() { + return usemizanCall('http_only_echo', 'http') +} + +/** + * Call staff_only server function. + * Transport: http + */ +export function useStaffOnly() { + return usemizanCall('staff_only', 'http') +} + +/** + * Call superuser_only server function. + * Transport: http + */ +export function useSuperuserOnly() { + return usemizanCall('superuser_only', 'http') +} + +/** + * Call verified_only server function. + * Transport: http + */ +export function useVerifiedOnly() { + return usemizanCall('verified_only', 'http') +} + +/** + * Call multiply server function. + * Transport: http + */ +export function useMultiply() { + return usemizanCall('multiply', 'http') +} + +/** + * Call not_implemented_fn server function. + * Transport: http + */ +export function useNotImplementedFn() { + return usemizanCall('not_implemented_fn', 'http') +} + +/** + * Call buggy_fn server function. + * Transport: http + */ +export function useBuggyFn() { + return usemizanCall('buggy_fn', 'http') +} + +/** + * Call permission_check_fn server function. + * Transport: http + */ +export function usePermissionCheckFn() { + return usemizanCall('permission_check_fn', 'http') +} + +/** + * Call ws_whoami server function. + * Transport: websocket + */ +export function useWsWhoami() { + return usemizanCall('ws_whoami', 'websocket') +} + +/** + * Call jwt_obtain server function. + * Transport: http + */ +export function useJwtObtain() { + return usemizanCall('jwt_obtain', 'http') +} + +/** + * Call jwt_refresh server function. + * Transport: http + */ +export function useJwtRefresh() { + return usemizanCall('jwt_refresh', 'http') +} + +// ============================================================================ +// Re-exports from mizan library +// ============================================================================ + +export { usemizan, usemizanStatus, usePush, DjangoError } from 'mizan' +export type { ConnectionStatus, PushMessage, PushListener } from 'mizan' diff --git a/examples/django-react-site/harness/src/api/generated.djarea.schema.json b/examples/django-react-site/harness/src/api/generated.djarea.schema.json new file mode 100644 index 0000000..f591565 --- /dev/null +++ b/examples/django-react-site/harness/src/api/generated.djarea.schema.json @@ -0,0 +1,2656 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "mizan Server Functions", + "version": "1.0.0", + "description": "Auto-generated schema for mizan server functions" + }, + "paths": { + "/mizan/echo": { + "post": { + "operationId": "echo", + "summary": "Call echo", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/echoOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/echoInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "websocket", + "isContext": false + } + } + }, + "/mizan/add": { + "post": { + "operationId": "add", + "summary": "Call add", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/addOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/addInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "websocket", + "isContext": false + } + } + }, + "/mizan/whoami": { + "post": { + "operationId": "whoami", + "summary": "Call whoami", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/whoamiOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/http_only_echo": { + "post": { + "operationId": "httpOnlyEcho", + "summary": "Call http_only_echo", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/httpOnlyEchoOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/httpOnlyEchoInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/login.schema": { + "post": { + "operationId": "loginSchema", + "summary": "Call login.schema", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/loginSchemaOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/login.validate": { + "post": { + "operationId": "loginValidate", + "summary": "Call login.validate", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/loginValidateOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/loginValidateInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/login.submit": { + "post": { + "operationId": "loginSubmit", + "summary": "Call login.submit", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/loginSubmitOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/loginSubmitInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/signup.schema": { + "post": { + "operationId": "signupSchema", + "summary": "Call signup.schema", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/signupSchemaOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/signup.validate": { + "post": { + "operationId": "signupValidate", + "summary": "Call signup.validate", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/signupValidateOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/signupValidateInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/signup.submit": { + "post": { + "operationId": "signupSubmit", + "summary": "Call signup.submit", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/signupSubmitOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/signupSubmitInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/add_email.schema": { + "post": { + "operationId": "addEmailSchema", + "summary": "Call add_email.schema", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/addEmailSchemaOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/add_email.validate": { + "post": { + "operationId": "addEmailValidate", + "summary": "Call add_email.validate", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/addEmailValidateOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/addEmailValidateInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/staff_only": { + "post": { + "operationId": "staffOnly", + "summary": "Call staff_only", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/staffOnlyOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/superuser_only": { + "post": { + "operationId": "superuserOnly", + "summary": "Call superuser_only", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/superuserOnlyOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/verified_only": { + "post": { + "operationId": "verifiedOnly", + "summary": "Call verified_only", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/verifiedOnlyOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/current_user": { + "post": { + "operationId": "currentUser", + "summary": "Call current_user", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/currentUserOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": "global" + } + } + }, + "/mizan/greet": { + "post": { + "operationId": "greet", + "summary": "Call greet", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/greetOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/greetInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": "local" + } + } + }, + "/mizan/multiply": { + "post": { + "operationId": "multiply", + "summary": "Call multiply", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/multiplyOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/multiplyInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/not_implemented_fn": { + "post": { + "operationId": "notImplementedFn", + "summary": "Call not_implemented_fn", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/notImplementedFnOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/buggy_fn": { + "post": { + "operationId": "buggyFn", + "summary": "Call buggy_fn", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/buggyFnOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/permission_check_fn": { + "post": { + "operationId": "permissionCheckFn", + "summary": "Call permission_check_fn", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/permissionCheckFnOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/permissionCheckFnInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/ws_whoami": { + "post": { + "operationId": "wsWhoami", + "summary": "Call ws_whoami", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/wsWhoamiOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "websocket", + "isContext": false + } + } + }, + "/mizan/contact.schema": { + "post": { + "operationId": "contactSchema", + "summary": "Call contact.schema", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/contactSchemaOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/contactSchemaInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/contact.validate": { + "post": { + "operationId": "contactValidate", + "summary": "Call contact.validate", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/contactValidateOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/contactValidateInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/contact.submit": { + "post": { + "operationId": "contactSubmit", + "summary": "\nSubmit function handles both JSON and multipart/form-data.\n\nThe executor detects form functions and parses the request appropriately.\n", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/contactSubmitOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/item.schema": { + "post": { + "operationId": "itemSchema", + "summary": "Call item.schema", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itemSchemaOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itemSchemaInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/item.validate": { + "post": { + "operationId": "itemValidate", + "summary": "Call item.validate", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itemValidateOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itemValidateInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/item.submit": { + "post": { + "operationId": "itemSubmit", + "summary": "\nSubmit function handles both JSON and multipart/form-data.\n\nThe executor detects form functions and parses the request appropriately.\n", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itemSubmitOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/item.formset.schema": { + "post": { + "operationId": "itemFormsetSchema", + "summary": "Call item.formset.schema", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itemFormsetSchemaOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itemFormsetSchemaInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/item.formset.validate": { + "post": { + "operationId": "itemFormsetValidate", + "summary": "Call item.formset.validate", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itemFormsetValidateOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itemFormsetValidateInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/item.formset.submit": { + "post": { + "operationId": "itemFormsetSubmit", + "summary": "Call item.formset.submit", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itemFormsetSubmitOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itemFormsetSubmitInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/jwt_obtain": { + "post": { + "operationId": "jwtObtain", + "summary": "\nObtain JWT tokens from an authenticated session.\n\nRequires session authentication (cookie or WebSocket session).\nReturns access and refresh tokens that can be used for stateless auth.\n\nThe tokens include user claims (is_staff, is_superuser) so that\nsubsequent JWT-authenticated requests don't need a database query.\n\nUsage:\n const { access_token, refresh_token } = await call('jwt_obtain')\n // Use access_token in Authorization: Bearer header\n", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jwtObtainOutput" + } + } + } + } + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + }, + "/mizan/jwt_refresh": { + "post": { + "operationId": "jwtRefresh", + "summary": "\nRefresh JWT tokens using a refresh token.\n\nDoes not require session authentication - the refresh token itself\ncontains the session reference and is validated against the session store.\n\nIf the original session has been destroyed (user logged out), this fails.\n\nUsage:\n const { access_token, refresh_token } = await call('jwt_refresh', { refresh_token })\n", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jwtRefreshOutput" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jwtRefreshInput" + } + } + }, + "required": true + }, + "x-mizan": { + "transport": "http", + "isContext": false + } + } + } + }, + "components": { + "schemas": { + "echoOutput": { + "properties": { + "message": { + "title": "Message", + "type": "string" + } + }, + "required": [ + "message" + ], + "title": "echoOutput", + "type": "object" + }, + "echoInput": { + "properties": { + "text": { + "title": "Text", + "type": "string" + } + }, + "required": [ + "text" + ], + "title": "echoInput", + "type": "object" + }, + "addOutput": { + "properties": { + "result": { + "title": "Result", + "type": "integer" + } + }, + "required": [ + "result" + ], + "title": "addOutput", + "type": "object" + }, + "addInput": { + "properties": { + "a": { + "title": "A", + "type": "integer" + }, + "b": { + "title": "B", + "type": "integer" + } + }, + "required": [ + "a", + "b" + ], + "title": "addInput", + "type": "object" + }, + "whoamiOutput": { + "properties": { + "user_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "User Id" + }, + "email": { + "title": "Email", + "type": "string" + }, + "is_staff": { + "title": "Is Staff", + "type": "boolean" + } + }, + "required": [ + "user_id", + "email", + "is_staff" + ], + "title": "whoamiOutput", + "type": "object" + }, + "httpOnlyEchoOutput": { + "properties": { + "message": { + "title": "Message", + "type": "string" + } + }, + "required": [ + "message" + ], + "title": "httpOnlyEchoOutput", + "type": "object" + }, + "httpOnlyEchoInput": { + "properties": { + "text": { + "title": "Text", + "type": "string" + } + }, + "required": [ + "text" + ], + "title": "httpOnlyEchoInput", + "type": "object" + }, + "FormSchemaField": { + "description": "Schema for a single form field.", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "title": "Type", + "type": "string" + }, + "required": { + "title": "Required", + "type": "boolean" + }, + "label": { + "title": "Label", + "type": "string" + }, + "help_text": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Help Text" + }, + "choices": { + "anyOf": [ + { + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Choices" + }, + "initial": { + "title": "Initial" + } + }, + "required": [ + "name", + "type", + "required", + "label" + ], + "title": "FormSchemaField", + "type": "object" + }, + "loginSchemaOutput": { + "properties": { + "fields": { + "items": { + "$ref": "#/components/schemas/FormSchemaField" + }, + "title": "Fields", + "type": "array" + } + }, + "required": [ + "fields" + ], + "title": "loginSchemaOutput", + "type": "object" + }, + "loginValidateOutput": { + "properties": { + "valid": { + "title": "Valid", + "type": "boolean" + }, + "errors": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "title": "Errors", + "type": "object" + } + }, + "required": [ + "valid", + "errors" + ], + "title": "loginValidateOutput", + "type": "object" + }, + "loginValidateInput": { + "properties": { + "data": { + "additionalProperties": true, + "title": "Data", + "type": "object" + } + }, + "required": [ + "data" + ], + "title": "loginValidateInput", + "type": "object" + }, + "loginSubmitOutput": { + "properties": {}, + "title": "loginSubmitOutput", + "type": "object" + }, + "loginSubmitInput": { + "properties": { + "data": { + "additionalProperties": true, + "title": "Data", + "type": "object" + } + }, + "required": [ + "data" + ], + "title": "loginSubmitInput", + "type": "object" + }, + "signupSchemaOutput": { + "properties": { + "fields": { + "items": { + "$ref": "#/components/schemas/FormSchemaField" + }, + "title": "Fields", + "type": "array" + } + }, + "required": [ + "fields" + ], + "title": "signupSchemaOutput", + "type": "object" + }, + "signupValidateOutput": { + "properties": { + "valid": { + "title": "Valid", + "type": "boolean" + }, + "errors": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "title": "Errors", + "type": "object" + } + }, + "required": [ + "valid", + "errors" + ], + "title": "signupValidateOutput", + "type": "object" + }, + "signupValidateInput": { + "properties": { + "data": { + "additionalProperties": true, + "title": "Data", + "type": "object" + } + }, + "required": [ + "data" + ], + "title": "signupValidateInput", + "type": "object" + }, + "signupSubmitOutput": { + "properties": {}, + "title": "signupSubmitOutput", + "type": "object" + }, + "signupSubmitInput": { + "properties": { + "data": { + "additionalProperties": true, + "title": "Data", + "type": "object" + } + }, + "required": [ + "data" + ], + "title": "signupSubmitInput", + "type": "object" + }, + "addEmailSchemaOutput": { + "properties": { + "fields": { + "items": { + "$ref": "#/components/schemas/FormSchemaField" + }, + "title": "Fields", + "type": "array" + } + }, + "required": [ + "fields" + ], + "title": "addEmailSchemaOutput", + "type": "object" + }, + "addEmailValidateOutput": { + "properties": { + "valid": { + "title": "Valid", + "type": "boolean" + }, + "errors": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "title": "Errors", + "type": "object" + } + }, + "required": [ + "valid", + "errors" + ], + "title": "addEmailValidateOutput", + "type": "object" + }, + "addEmailValidateInput": { + "properties": { + "data": { + "additionalProperties": true, + "title": "Data", + "type": "object" + } + }, + "required": [ + "data" + ], + "title": "addEmailValidateInput", + "type": "object" + }, + "staffOnlyOutput": { + "properties": { + "message": { + "title": "Message", + "type": "string" + } + }, + "required": [ + "message" + ], + "title": "staffOnlyOutput", + "type": "object" + }, + "superuserOnlyOutput": { + "properties": { + "message": { + "title": "Message", + "type": "string" + } + }, + "required": [ + "message" + ], + "title": "superuserOnlyOutput", + "type": "object" + }, + "verifiedOnlyOutput": { + "properties": { + "message": { + "title": "Message", + "type": "string" + } + }, + "required": [ + "message" + ], + "title": "verifiedOnlyOutput", + "type": "object" + }, + "currentUserOutput": { + "properties": { + "authenticated": { + "title": "Authenticated", + "type": "boolean" + }, + "email": { + "title": "Email", + "type": "string" + }, + "is_staff": { + "title": "Is Staff", + "type": "boolean" + } + }, + "required": [ + "authenticated", + "email", + "is_staff" + ], + "title": "currentUserOutput", + "type": "object" + }, + "greetOutput": { + "properties": { + "greeting": { + "title": "Greeting", + "type": "string" + } + }, + "required": [ + "greeting" + ], + "title": "greetOutput", + "type": "object" + }, + "greetInput": { + "properties": { + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "name" + ], + "title": "greetInput", + "type": "object" + }, + "multiplyOutput": { + "properties": { + "product": { + "title": "Product", + "type": "integer" + } + }, + "required": [ + "product" + ], + "title": "multiplyOutput", + "type": "object" + }, + "multiplyInput": { + "properties": { + "x": { + "title": "X", + "type": "integer" + }, + "y": { + "title": "Y", + "type": "integer" + } + }, + "required": [ + "x", + "y" + ], + "title": "multiplyInput", + "type": "object" + }, + "notImplementedFnOutput": { + "properties": { + "message": { + "title": "Message", + "type": "string" + } + }, + "required": [ + "message" + ], + "title": "notImplementedFnOutput", + "type": "object" + }, + "buggyFnOutput": { + "properties": { + "message": { + "title": "Message", + "type": "string" + } + }, + "required": [ + "message" + ], + "title": "buggyFnOutput", + "type": "object" + }, + "permissionCheckFnOutput": { + "properties": { + "message": { + "title": "Message", + "type": "string" + } + }, + "required": [ + "message" + ], + "title": "permissionCheckFnOutput", + "type": "object" + }, + "permissionCheckFnInput": { + "properties": { + "secret": { + "title": "Secret", + "type": "string" + } + }, + "required": [ + "secret" + ], + "title": "permissionCheckFnInput", + "type": "object" + }, + "wsWhoamiOutput": { + "properties": { + "user_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "User Id" + }, + "email": { + "title": "Email", + "type": "string" + }, + "is_staff": { + "title": "Is Staff", + "type": "boolean" + } + }, + "required": [ + "user_id", + "email", + "is_staff" + ], + "title": "wsWhoamiOutput", + "type": "object" + }, + "FieldChoice": { + "properties": { + "value": { + "title": "Value", + "type": "string" + }, + "label": { + "title": "Label", + "type": "string" + } + }, + "required": [ + "value", + "label" + ], + "title": "FieldChoice", + "type": "object" + }, + "FieldSchema": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "label": { + "title": "Label", + "type": "string" + }, + "type": { + "title": "Type", + "type": "string" + }, + "widget": { + "title": "Widget", + "type": "string" + }, + "required": { + "title": "Required", + "type": "boolean" + }, + "disabled": { + "title": "Disabled", + "type": "boolean" + }, + "help_text": { + "title": "Help Text", + "type": "string" + }, + "initial": { + "title": "Initial" + }, + "max_length": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Max Length" + }, + "min_length": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Min Length" + }, + "choices": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/FieldChoice" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Choices" + } + }, + "required": [ + "name", + "label", + "type", + "widget", + "required", + "disabled", + "help_text", + "initial", + "max_length", + "min_length", + "choices" + ], + "title": "FieldSchema", + "type": "object" + }, + "FormMeta": { + "description": "Metadata controlling frontend form behavior.\n\nAttributes:\n refetch_schema_on_validate: If True, frontend should refetch schema on each\n validation (useful for dynamic choice fields). Default False.\n live_validation: If False, frontend should disable live validation entirely.\n Useful for sensitive forms like login. Default True.\n live_form_errors: If True, show form-level errors during live validation.\n Form errors are things like \"Invalid credentials\" vs field errors like\n \"This field is required\". Default False for security.", + "properties": { + "refetch_schema_on_validate": { + "default": false, + "title": "Refetch Schema On Validate", + "type": "boolean" + }, + "live_validation": { + "default": true, + "title": "Live Validation", + "type": "boolean" + }, + "live_form_errors": { + "default": false, + "title": "Live Form Errors", + "type": "boolean" + } + }, + "title": "FormMeta", + "type": "object" + }, + "contactSchemaOutput": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "title": { + "title": "Title", + "type": "string" + }, + "subtitle": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Subtitle" + }, + "submit_label": { + "title": "Submit Label", + "type": "string" + }, + "fields": { + "items": { + "$ref": "#/components/schemas/FieldSchema" + }, + "title": "Fields", + "type": "array" + }, + "meta": { + "allOf": [ + { + "$ref": "#/components/schemas/FormMeta" + } + ], + "default": { + "refetch_schema_on_validate": false, + "live_validation": true, + "live_form_errors": false + } + } + }, + "required": [ + "name", + "title", + "subtitle", + "submit_label", + "fields" + ], + "title": "contactSchemaOutput", + "type": "object" + }, + "contactSchemaInput": { + "properties": { + "data": { + "additionalProperties": true, + "default": {}, + "title": "Data", + "type": "object" + } + }, + "title": "contactSchemaInput", + "type": "object" + }, + "FieldError": { + "properties": { + "message": { + "title": "Message", + "type": "string" + }, + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + } + }, + "required": [ + "message", + "code" + ], + "title": "FieldError", + "type": "object" + }, + "FieldErrorList": { + "properties": { + "field": { + "title": "Field", + "type": "string" + }, + "errors": { + "items": { + "$ref": "#/components/schemas/FieldError" + }, + "title": "Errors", + "type": "array" + } + }, + "required": [ + "field", + "errors" + ], + "title": "FieldErrorList", + "type": "object" + }, + "contactValidateOutput": { + "properties": { + "errors": { + "items": { + "$ref": "#/components/schemas/FieldErrorList" + }, + "title": "Errors", + "type": "array" + } + }, + "required": [ + "errors" + ], + "title": "contactValidateOutput", + "type": "object" + }, + "contactValidateInput": { + "properties": { + "data": { + "additionalProperties": true, + "title": "Data", + "type": "object" + } + }, + "required": [ + "data" + ], + "title": "contactValidateInput", + "type": "object" + }, + "contactSubmitOutput": { + "properties": { + "success": { + "title": "Success", + "type": "boolean" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + } + }, + "required": [ + "success" + ], + "title": "contactSubmitOutput", + "type": "object" + }, + "itemSchemaOutput": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "title": { + "title": "Title", + "type": "string" + }, + "subtitle": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Subtitle" + }, + "submit_label": { + "title": "Submit Label", + "type": "string" + }, + "fields": { + "items": { + "$ref": "#/components/schemas/FieldSchema" + }, + "title": "Fields", + "type": "array" + }, + "meta": { + "allOf": [ + { + "$ref": "#/components/schemas/FormMeta" + } + ], + "default": { + "refetch_schema_on_validate": false, + "live_validation": true, + "live_form_errors": false + } + } + }, + "required": [ + "name", + "title", + "subtitle", + "submit_label", + "fields" + ], + "title": "itemSchemaOutput", + "type": "object" + }, + "itemSchemaInput": { + "properties": { + "data": { + "additionalProperties": true, + "default": {}, + "title": "Data", + "type": "object" + } + }, + "title": "itemSchemaInput", + "type": "object" + }, + "itemValidateOutput": { + "properties": { + "errors": { + "items": { + "$ref": "#/components/schemas/FieldErrorList" + }, + "title": "Errors", + "type": "array" + } + }, + "required": [ + "errors" + ], + "title": "itemValidateOutput", + "type": "object" + }, + "itemValidateInput": { + "properties": { + "data": { + "additionalProperties": true, + "title": "Data", + "type": "object" + } + }, + "required": [ + "data" + ], + "title": "itemValidateInput", + "type": "object" + }, + "itemSubmitOutput": { + "properties": { + "success": { + "title": "Success", + "type": "boolean" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + } + }, + "required": [ + "success" + ], + "title": "itemSubmitOutput", + "type": "object" + }, + "FormSchema": { + "description": "Schema returned by /schema endpoint with form metadata and fields.", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "title": { + "title": "Title", + "type": "string" + }, + "subtitle": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Subtitle" + }, + "submit_label": { + "title": "Submit Label", + "type": "string" + }, + "fields": { + "items": { + "$ref": "#/components/schemas/FieldSchema" + }, + "title": "Fields", + "type": "array" + }, + "meta": { + "allOf": [ + { + "$ref": "#/components/schemas/FormMeta" + } + ], + "default": { + "refetch_schema_on_validate": false, + "live_validation": true, + "live_form_errors": false + } + } + }, + "required": [ + "name", + "title", + "subtitle", + "submit_label", + "fields" + ], + "title": "FormSchema", + "type": "object" + }, + "itemFormsetSchemaOutput": { + "properties": { + "forms": { + "items": { + "$ref": "#/components/schemas/FormSchema" + }, + "title": "Forms", + "type": "array" + }, + "min_num": { + "title": "Min Num", + "type": "integer" + }, + "max_num": { + "title": "Max Num", + "type": "integer" + }, + "can_delete": { + "title": "Can Delete", + "type": "boolean" + }, + "can_order": { + "title": "Can Order", + "type": "boolean" + } + }, + "required": [ + "forms", + "min_num", + "max_num", + "can_delete", + "can_order" + ], + "title": "itemFormsetSchemaOutput", + "type": "object" + }, + "itemFormsetSchemaInput": { + "properties": { + "forms": { + "default": [], + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Forms", + "type": "array" + } + }, + "title": "itemFormsetSchemaInput", + "type": "object" + }, + "FormValidation": { + "properties": { + "errors": { + "items": { + "$ref": "#/components/schemas/FieldErrorList" + }, + "title": "Errors", + "type": "array" + } + }, + "required": [ + "errors" + ], + "title": "FormValidation", + "type": "object" + }, + "itemFormsetValidateOutput": { + "properties": { + "general": { + "items": { + "type": "string" + }, + "title": "General", + "type": "array" + }, + "per_form": { + "items": { + "$ref": "#/components/schemas/FormValidation" + }, + "title": "Per Form", + "type": "array" + } + }, + "required": [ + "general", + "per_form" + ], + "title": "itemFormsetValidateOutput", + "type": "object" + }, + "itemFormsetValidateInput": { + "properties": { + "forms": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Forms", + "type": "array" + } + }, + "required": [ + "forms" + ], + "title": "itemFormsetValidateInput", + "type": "object" + }, + "itemFormsetSubmitOutput": { + "properties": { + "success": { + "title": "Success", + "type": "boolean" + } + }, + "required": [ + "success" + ], + "title": "itemFormsetSubmitOutput", + "type": "object" + }, + "itemFormsetSubmitInput": { + "properties": { + "forms": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Forms", + "type": "array" + } + }, + "required": [ + "forms" + ], + "title": "itemFormsetSubmitInput", + "type": "object" + }, + "jwtObtainOutput": { + "properties": { + "access_token": { + "title": "Access Token", + "type": "string" + }, + "refresh_token": { + "title": "Refresh Token", + "type": "string" + }, + "expires_in": { + "title": "Expires In", + "type": "integer" + } + }, + "required": [ + "access_token", + "refresh_token", + "expires_in" + ], + "title": "jwtObtainOutput", + "type": "object" + }, + "jwtRefreshOutput": { + "properties": { + "access_token": { + "title": "Access Token", + "type": "string" + }, + "refresh_token": { + "title": "Refresh Token", + "type": "string" + }, + "expires_in": { + "title": "Expires In", + "type": "integer" + } + }, + "required": [ + "access_token", + "refresh_token", + "expires_in" + ], + "title": "jwtRefreshOutput", + "type": "object" + }, + "jwtRefreshInput": { + "properties": { + "refresh_token": { + "title": "Refresh Token", + "type": "string" + } + }, + "required": [ + "refresh_token" + ], + "title": "jwtRefreshInput", + "type": "object" + } + } + }, + "servers": [], + "x-mizan-functions": [ + { + "name": "echo", + "camelName": "echo", + "hasInput": true, + "inputType": "echoInput", + "outputType": "echoOutput", + "transport": "websocket", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "add", + "camelName": "add", + "hasInput": true, + "inputType": "addInput", + "outputType": "addOutput", + "transport": "websocket", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "whoami", + "camelName": "whoami", + "hasInput": false, + "inputType": null, + "outputType": "whoamiOutput", + "transport": "http", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "http_only_echo", + "camelName": "httpOnlyEcho", + "hasInput": true, + "inputType": "httpOnlyEchoInput", + "outputType": "httpOnlyEchoOutput", + "transport": "http", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "login.schema", + "camelName": "loginSchema", + "hasInput": false, + "inputType": null, + "outputType": "loginSchemaOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "login", + "formRole": "schema" + }, + { + "name": "login.validate", + "camelName": "loginValidate", + "hasInput": true, + "inputType": "loginValidateInput", + "outputType": "loginValidateOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "login", + "formRole": "validate" + }, + { + "name": "login.submit", + "camelName": "loginSubmit", + "hasInput": true, + "inputType": "loginSubmitInput", + "outputType": "loginSubmitOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "login", + "formRole": "submit" + }, + { + "name": "signup.schema", + "camelName": "signupSchema", + "hasInput": false, + "inputType": null, + "outputType": "signupSchemaOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "signup", + "formRole": "schema" + }, + { + "name": "signup.validate", + "camelName": "signupValidate", + "hasInput": true, + "inputType": "signupValidateInput", + "outputType": "signupValidateOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "signup", + "formRole": "validate" + }, + { + "name": "signup.submit", + "camelName": "signupSubmit", + "hasInput": true, + "inputType": "signupSubmitInput", + "outputType": "signupSubmitOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "signup", + "formRole": "submit" + }, + { + "name": "add_email.schema", + "camelName": "addEmailSchema", + "hasInput": false, + "inputType": null, + "outputType": "addEmailSchemaOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "add_email", + "formRole": "schema" + }, + { + "name": "add_email.validate", + "camelName": "addEmailValidate", + "hasInput": true, + "inputType": "addEmailValidateInput", + "outputType": "addEmailValidateOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "add_email", + "formRole": "validate" + }, + { + "name": "staff_only", + "camelName": "staffOnly", + "hasInput": false, + "inputType": null, + "outputType": "staffOnlyOutput", + "transport": "http", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "superuser_only", + "camelName": "superuserOnly", + "hasInput": false, + "inputType": null, + "outputType": "superuserOnlyOutput", + "transport": "http", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "verified_only", + "camelName": "verifiedOnly", + "hasInput": false, + "inputType": null, + "outputType": "verifiedOnlyOutput", + "transport": "http", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "current_user", + "camelName": "currentUser", + "hasInput": false, + "inputType": null, + "outputType": "currentUserOutput", + "transport": "http", + "isContext": "global", + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "greet", + "camelName": "greet", + "hasInput": true, + "inputType": "greetInput", + "outputType": "greetOutput", + "transport": "http", + "isContext": "local", + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "multiply", + "camelName": "multiply", + "hasInput": true, + "inputType": "multiplyInput", + "outputType": "multiplyOutput", + "transport": "http", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "not_implemented_fn", + "camelName": "notImplementedFn", + "hasInput": false, + "inputType": null, + "outputType": "notImplementedFnOutput", + "transport": "http", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "buggy_fn", + "camelName": "buggyFn", + "hasInput": false, + "inputType": null, + "outputType": "buggyFnOutput", + "transport": "http", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "permission_check_fn", + "camelName": "permissionCheckFn", + "hasInput": true, + "inputType": "permissionCheckFnInput", + "outputType": "permissionCheckFnOutput", + "transport": "http", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "ws_whoami", + "camelName": "wsWhoami", + "hasInput": false, + "inputType": null, + "outputType": "wsWhoamiOutput", + "transport": "websocket", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "contact.schema", + "camelName": "contactSchema", + "hasInput": true, + "inputType": "contactSchemaInput", + "outputType": "contactSchemaOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "contact", + "formRole": "schema", + "formFields": [ + { + "name": "name", + "required": true, + "constraints": { + "max": 100 + }, + "zodType": "string" + }, + { + "name": "email", + "required": true, + "constraints": { + "max": 320, + "email": true + }, + "zodType": "string" + }, + { + "name": "message", + "required": true, + "constraints": {}, + "zodType": "string" + } + ] + }, + { + "name": "contact.validate", + "camelName": "contactValidate", + "hasInput": true, + "inputType": "contactValidateInput", + "outputType": "contactValidateOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "contact", + "formRole": "validate" + }, + { + "name": "contact.submit", + "camelName": "contactSubmit", + "hasInput": false, + "inputType": null, + "outputType": "contactSubmitOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "contact", + "formRole": "submit" + }, + { + "name": "item.schema", + "camelName": "itemSchema", + "hasInput": true, + "inputType": "itemSchemaInput", + "outputType": "itemSchemaOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "item", + "formRole": "schema", + "formFields": [ + { + "name": "label", + "required": true, + "constraints": { + "max": 50 + }, + "zodType": "string" + }, + { + "name": "quantity", + "required": true, + "constraints": { + "int": true, + "min": 1 + }, + "zodType": "number" + } + ] + }, + { + "name": "item.validate", + "camelName": "itemValidate", + "hasInput": true, + "inputType": "itemValidateInput", + "outputType": "itemValidateOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "item", + "formRole": "validate" + }, + { + "name": "item.submit", + "camelName": "itemSubmit", + "hasInput": false, + "inputType": null, + "outputType": "itemSubmitOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "item", + "formRole": "submit" + }, + { + "name": "item.formset.schema", + "camelName": "itemFormsetSchema", + "hasInput": true, + "inputType": "itemFormsetSchemaInput", + "outputType": "itemFormsetSchemaOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "item", + "formRole": "formset_schema" + }, + { + "name": "item.formset.validate", + "camelName": "itemFormsetValidate", + "hasInput": true, + "inputType": "itemFormsetValidateInput", + "outputType": "itemFormsetValidateOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "item", + "formRole": "formset_validate" + }, + { + "name": "item.formset.submit", + "camelName": "itemFormsetSubmit", + "hasInput": true, + "inputType": "itemFormsetSubmitInput", + "outputType": "itemFormsetSubmitOutput", + "transport": "http", + "isContext": false, + "isForm": true, + "formName": "item", + "formRole": "formset_submit" + }, + { + "name": "jwt_obtain", + "camelName": "jwtObtain", + "hasInput": false, + "inputType": null, + "outputType": "jwtObtainOutput", + "transport": "http", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + }, + { + "name": "jwt_refresh", + "camelName": "jwtRefresh", + "hasInput": true, + "inputType": "jwtRefreshInput", + "outputType": "jwtRefreshOutput", + "transport": "http", + "isContext": false, + "isForm": false, + "formName": null, + "formRole": null + } + ] +} \ No newline at end of file diff --git a/examples/django-react-site/harness/src/api/generated.djarea.ts b/examples/django-react-site/harness/src/api/generated.djarea.ts new file mode 100644 index 0000000..5d6a652 --- /dev/null +++ b/examples/django-react-site/harness/src/api/generated.djarea.ts @@ -0,0 +1,2123 @@ +// AUTO-GENERATED by mizan - do not edit manually +// Regenerate with: npm run schemas + +// ============================================================================ +// OpenAPI Types (generated by openapi-typescript) +// ============================================================================ + +export interface paths { + "/mizan/echo": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call echo */ + post: operations["echo"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/add": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call add */ + post: operations["add"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/whoami": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call whoami */ + post: operations["whoami"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/http_only_echo": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call http_only_echo */ + post: operations["httpOnlyEcho"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/login.schema": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call login.schema */ + post: operations["loginSchema"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/login.validate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call login.validate */ + post: operations["loginValidate"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/login.submit": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call login.submit */ + post: operations["loginSubmit"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/signup.schema": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call signup.schema */ + post: operations["signupSchema"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/signup.validate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call signup.validate */ + post: operations["signupValidate"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/signup.submit": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call signup.submit */ + post: operations["signupSubmit"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/add_email.schema": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call add_email.schema */ + post: operations["addEmailSchema"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/add_email.validate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call add_email.validate */ + post: operations["addEmailValidate"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/staff_only": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call staff_only */ + post: operations["staffOnly"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/superuser_only": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call superuser_only */ + post: operations["superuserOnly"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/verified_only": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call verified_only */ + post: operations["verifiedOnly"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/current_user": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call current_user */ + post: operations["currentUser"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/greet": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call greet */ + post: operations["greet"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/multiply": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call multiply */ + post: operations["multiply"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/not_implemented_fn": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call not_implemented_fn */ + post: operations["notImplementedFn"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/buggy_fn": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call buggy_fn */ + post: operations["buggyFn"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/permission_check_fn": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call permission_check_fn */ + post: operations["permissionCheckFn"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/ws_whoami": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call ws_whoami */ + post: operations["wsWhoami"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/contact.schema": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call contact.schema */ + post: operations["contactSchema"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/contact.validate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call contact.validate */ + post: operations["contactValidate"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/contact.submit": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Submit function handles both JSON and multipart/form-data. + * + * The executor detects form functions and parses the request appropriately. + */ + post: operations["contactSubmit"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/item.schema": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call item.schema */ + post: operations["itemSchema"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/item.validate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call item.validate */ + post: operations["itemValidate"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/item.submit": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Submit function handles both JSON and multipart/form-data. + * + * The executor detects form functions and parses the request appropriately. + */ + post: operations["itemSubmit"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/item.formset.schema": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call item.formset.schema */ + post: operations["itemFormsetSchema"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/item.formset.validate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call item.formset.validate */ + post: operations["itemFormsetValidate"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/item.formset.submit": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Call item.formset.submit */ + post: operations["itemFormsetSubmit"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/jwt_obtain": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Obtain JWT tokens from an authenticated session. + * + * Requires session authentication (cookie or WebSocket session). + * Returns access and refresh tokens that can be used for stateless auth. + * + * The tokens include user claims (is_staff, is_superuser) so that + * subsequent JWT-authenticated requests don't need a database query. + * + * Usage: + * const { access_token, refresh_token } = await call('jwt_obtain') + * // Use access_token in Authorization: Bearer header + */ + post: operations["jwtObtain"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/mizan/jwt_refresh": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Refresh JWT tokens using a refresh token. + * + * Does not require session authentication - the refresh token itself + * contains the session reference and is validated against the session store. + * + * If the original session has been destroyed (user logged out), this fails. + * + * Usage: + * const { access_token, refresh_token } = await call('jwt_refresh', { refresh_token }) + */ + post: operations["jwtRefresh"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + /** echoOutput */ + echoOutput: { + /** Message */ + message: string; + }; + /** echoInput */ + echoInput: { + /** Text */ + text: string; + }; + /** addOutput */ + addOutput: { + /** Result */ + result: number; + }; + /** addInput */ + addInput: { + /** A */ + a: number; + /** B */ + b: number; + }; + /** whoamiOutput */ + whoamiOutput: { + /** User Id */ + user_id: number | null; + /** Email */ + email: string; + /** Is Staff */ + is_staff: boolean; + }; + /** httpOnlyEchoOutput */ + httpOnlyEchoOutput: { + /** Message */ + message: string; + }; + /** httpOnlyEchoInput */ + httpOnlyEchoInput: { + /** Text */ + text: string; + }; + /** + * FormSchemaField + * @description Schema for a single form field. + */ + FormSchemaField: { + /** Name */ + name: string; + /** Type */ + type: string; + /** Required */ + required: boolean; + /** Label */ + label: string; + /** Help Text */ + help_text?: string | null; + /** Choices */ + choices?: [ + string, + string + ][] | null; + /** Initial */ + initial?: unknown; + }; + /** loginSchemaOutput */ + loginSchemaOutput: { + /** Fields */ + fields: components["schemas"]["FormSchemaField"][]; + }; + /** loginValidateOutput */ + loginValidateOutput: { + /** Valid */ + valid: boolean; + /** Errors */ + errors: { + [key: string]: string[]; + }; + }; + /** loginValidateInput */ + loginValidateInput: { + /** Data */ + data: { + [key: string]: unknown; + }; + }; + /** loginSubmitOutput */ + loginSubmitOutput: Record; + /** loginSubmitInput */ + loginSubmitInput: { + /** Data */ + data: { + [key: string]: unknown; + }; + }; + /** signupSchemaOutput */ + signupSchemaOutput: { + /** Fields */ + fields: components["schemas"]["FormSchemaField"][]; + }; + /** signupValidateOutput */ + signupValidateOutput: { + /** Valid */ + valid: boolean; + /** Errors */ + errors: { + [key: string]: string[]; + }; + }; + /** signupValidateInput */ + signupValidateInput: { + /** Data */ + data: { + [key: string]: unknown; + }; + }; + /** signupSubmitOutput */ + signupSubmitOutput: Record; + /** signupSubmitInput */ + signupSubmitInput: { + /** Data */ + data: { + [key: string]: unknown; + }; + }; + /** addEmailSchemaOutput */ + addEmailSchemaOutput: { + /** Fields */ + fields: components["schemas"]["FormSchemaField"][]; + }; + /** addEmailValidateOutput */ + addEmailValidateOutput: { + /** Valid */ + valid: boolean; + /** Errors */ + errors: { + [key: string]: string[]; + }; + }; + /** addEmailValidateInput */ + addEmailValidateInput: { + /** Data */ + data: { + [key: string]: unknown; + }; + }; + /** staffOnlyOutput */ + staffOnlyOutput: { + /** Message */ + message: string; + }; + /** superuserOnlyOutput */ + superuserOnlyOutput: { + /** Message */ + message: string; + }; + /** verifiedOnlyOutput */ + verifiedOnlyOutput: { + /** Message */ + message: string; + }; + /** currentUserOutput */ + currentUserOutput: { + /** Authenticated */ + authenticated: boolean; + /** Email */ + email: string; + /** Is Staff */ + is_staff: boolean; + }; + /** greetOutput */ + greetOutput: { + /** Greeting */ + greeting: string; + }; + /** greetInput */ + greetInput: { + /** Name */ + name: string; + }; + /** multiplyOutput */ + multiplyOutput: { + /** Product */ + product: number; + }; + /** multiplyInput */ + multiplyInput: { + /** X */ + x: number; + /** Y */ + y: number; + }; + /** notImplementedFnOutput */ + notImplementedFnOutput: { + /** Message */ + message: string; + }; + /** buggyFnOutput */ + buggyFnOutput: { + /** Message */ + message: string; + }; + /** permissionCheckFnOutput */ + permissionCheckFnOutput: { + /** Message */ + message: string; + }; + /** permissionCheckFnInput */ + permissionCheckFnInput: { + /** Secret */ + secret: string; + }; + /** wsWhoamiOutput */ + wsWhoamiOutput: { + /** User Id */ + user_id: number | null; + /** Email */ + email: string; + /** Is Staff */ + is_staff: boolean; + }; + /** FieldChoice */ + FieldChoice: { + /** Value */ + value: string; + /** Label */ + label: string; + }; + /** FieldSchema */ + FieldSchema: { + /** Name */ + name: string; + /** Label */ + label: string; + /** Type */ + type: string; + /** Widget */ + widget: string; + /** Required */ + required: boolean; + /** Disabled */ + disabled: boolean; + /** Help Text */ + help_text: string; + /** Initial */ + initial: unknown; + /** Max Length */ + max_length: number | null; + /** Min Length */ + min_length: number | null; + /** Choices */ + choices: components["schemas"]["FieldChoice"][] | null; + }; + /** + * FormMeta + * @description Metadata controlling frontend form behavior. + * + * Attributes: + * refetch_schema_on_validate: If True, frontend should refetch schema on each + * validation (useful for dynamic choice fields). Default False. + * live_validation: If False, frontend should disable live validation entirely. + * Useful for sensitive forms like login. Default True. + * live_form_errors: If True, show form-level errors during live validation. + * Form errors are things like "Invalid credentials" vs field errors like + * "This field is required". Default False for security. + */ + FormMeta: { + /** + * Refetch Schema On Validate + * @default false + */ + refetch_schema_on_validate: boolean; + /** + * Live Validation + * @default true + */ + live_validation: boolean; + /** + * Live Form Errors + * @default false + */ + live_form_errors: boolean; + }; + /** contactSchemaOutput */ + contactSchemaOutput: { + /** Name */ + name: string; + /** Title */ + title: string; + /** Subtitle */ + subtitle: string | null; + /** Submit Label */ + submit_label: string; + /** Fields */ + fields: components["schemas"]["FieldSchema"][]; + /** + * @default { + * "refetch_schema_on_validate": false, + * "live_validation": true, + * "live_form_errors": false + * } + */ + meta: components["schemas"]["FormMeta"]; + }; + /** contactSchemaInput */ + contactSchemaInput: { + /** + * Data + * @default {} + */ + data: { + [key: string]: unknown; + }; + }; + /** FieldError */ + FieldError: { + /** Message */ + message: string; + /** Code */ + code: string | null; + }; + /** FieldErrorList */ + FieldErrorList: { + /** Field */ + field: string; + /** Errors */ + errors: components["schemas"]["FieldError"][]; + }; + /** contactValidateOutput */ + contactValidateOutput: { + /** Errors */ + errors: components["schemas"]["FieldErrorList"][]; + }; + /** contactValidateInput */ + contactValidateInput: { + /** Data */ + data: { + [key: string]: unknown; + }; + }; + /** contactSubmitOutput */ + contactSubmitOutput: { + /** Success */ + success: boolean; + /** Data */ + data?: { + [key: string]: unknown; + } | null; + }; + /** itemSchemaOutput */ + itemSchemaOutput: { + /** Name */ + name: string; + /** Title */ + title: string; + /** Subtitle */ + subtitle: string | null; + /** Submit Label */ + submit_label: string; + /** Fields */ + fields: components["schemas"]["FieldSchema"][]; + /** + * @default { + * "refetch_schema_on_validate": false, + * "live_validation": true, + * "live_form_errors": false + * } + */ + meta: components["schemas"]["FormMeta"]; + }; + /** itemSchemaInput */ + itemSchemaInput: { + /** + * Data + * @default {} + */ + data: { + [key: string]: unknown; + }; + }; + /** itemValidateOutput */ + itemValidateOutput: { + /** Errors */ + errors: components["schemas"]["FieldErrorList"][]; + }; + /** itemValidateInput */ + itemValidateInput: { + /** Data */ + data: { + [key: string]: unknown; + }; + }; + /** itemSubmitOutput */ + itemSubmitOutput: { + /** Success */ + success: boolean; + /** Data */ + data?: { + [key: string]: unknown; + } | null; + }; + /** + * FormSchema + * @description Schema returned by /schema endpoint with form metadata and fields. + */ + FormSchema: { + /** Name */ + name: string; + /** Title */ + title: string; + /** Subtitle */ + subtitle: string | null; + /** Submit Label */ + submit_label: string; + /** Fields */ + fields: components["schemas"]["FieldSchema"][]; + /** + * @default { + * "refetch_schema_on_validate": false, + * "live_validation": true, + * "live_form_errors": false + * } + */ + meta: components["schemas"]["FormMeta"]; + }; + /** itemFormsetSchemaOutput */ + itemFormsetSchemaOutput: { + /** Forms */ + forms: components["schemas"]["FormSchema"][]; + /** Min Num */ + min_num: number; + /** Max Num */ + max_num: number; + /** Can Delete */ + can_delete: boolean; + /** Can Order */ + can_order: boolean; + }; + /** itemFormsetSchemaInput */ + itemFormsetSchemaInput: { + /** + * Forms + * @default [] + */ + forms: { + [key: string]: unknown; + }[]; + }; + /** FormValidation */ + FormValidation: { + /** Errors */ + errors: components["schemas"]["FieldErrorList"][]; + }; + /** itemFormsetValidateOutput */ + itemFormsetValidateOutput: { + /** General */ + general: string[]; + /** Per Form */ + per_form: components["schemas"]["FormValidation"][]; + }; + /** itemFormsetValidateInput */ + itemFormsetValidateInput: { + /** Forms */ + forms: { + [key: string]: unknown; + }[]; + }; + /** itemFormsetSubmitOutput */ + itemFormsetSubmitOutput: { + /** Success */ + success: boolean; + }; + /** itemFormsetSubmitInput */ + itemFormsetSubmitInput: { + /** Forms */ + forms: { + [key: string]: unknown; + }[]; + }; + /** jwtObtainOutput */ + jwtObtainOutput: { + /** Access Token */ + access_token: string; + /** Refresh Token */ + refresh_token: string; + /** Expires In */ + expires_in: number; + }; + /** jwtRefreshOutput */ + jwtRefreshOutput: { + /** Access Token */ + access_token: string; + /** Refresh Token */ + refresh_token: string; + /** Expires In */ + expires_in: number; + }; + /** jwtRefreshInput */ + jwtRefreshInput: { + /** Refresh Token */ + refresh_token: string; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + echo: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["echoInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["echoOutput"]; + }; + }; + }; + }; + add: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["addInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["addOutput"]; + }; + }; + }; + }; + whoami: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["whoamiOutput"]; + }; + }; + }; + }; + httpOnlyEcho: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["httpOnlyEchoInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["httpOnlyEchoOutput"]; + }; + }; + }; + }; + loginSchema: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["loginSchemaOutput"]; + }; + }; + }; + }; + loginValidate: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["loginValidateInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["loginValidateOutput"]; + }; + }; + }; + }; + loginSubmit: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["loginSubmitInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["loginSubmitOutput"]; + }; + }; + }; + }; + signupSchema: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["signupSchemaOutput"]; + }; + }; + }; + }; + signupValidate: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["signupValidateInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["signupValidateOutput"]; + }; + }; + }; + }; + signupSubmit: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["signupSubmitInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["signupSubmitOutput"]; + }; + }; + }; + }; + addEmailSchema: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["addEmailSchemaOutput"]; + }; + }; + }; + }; + addEmailValidate: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["addEmailValidateInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["addEmailValidateOutput"]; + }; + }; + }; + }; + staffOnly: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["staffOnlyOutput"]; + }; + }; + }; + }; + superuserOnly: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["superuserOnlyOutput"]; + }; + }; + }; + }; + verifiedOnly: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["verifiedOnlyOutput"]; + }; + }; + }; + }; + currentUser: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["currentUserOutput"]; + }; + }; + }; + }; + greet: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["greetInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["greetOutput"]; + }; + }; + }; + }; + multiply: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["multiplyInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["multiplyOutput"]; + }; + }; + }; + }; + notImplementedFn: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["notImplementedFnOutput"]; + }; + }; + }; + }; + buggyFn: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["buggyFnOutput"]; + }; + }; + }; + }; + permissionCheckFn: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["permissionCheckFnInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["permissionCheckFnOutput"]; + }; + }; + }; + }; + wsWhoami: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["wsWhoamiOutput"]; + }; + }; + }; + }; + contactSchema: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["contactSchemaInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["contactSchemaOutput"]; + }; + }; + }; + }; + contactValidate: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["contactValidateInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["contactValidateOutput"]; + }; + }; + }; + }; + contactSubmit: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["contactSubmitOutput"]; + }; + }; + }; + }; + itemSchema: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["itemSchemaInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["itemSchemaOutput"]; + }; + }; + }; + }; + itemValidate: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["itemValidateInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["itemValidateOutput"]; + }; + }; + }; + }; + itemSubmit: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["itemSubmitOutput"]; + }; + }; + }; + }; + itemFormsetSchema: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["itemFormsetSchemaInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["itemFormsetSchemaOutput"]; + }; + }; + }; + }; + itemFormsetValidate: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["itemFormsetValidateInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["itemFormsetValidateOutput"]; + }; + }; + }; + }; + itemFormsetSubmit: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["itemFormsetSubmitInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["itemFormsetSubmitOutput"]; + }; + }; + }; + }; + jwtObtain: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["jwtObtainOutput"]; + }; + }; + }; + }; + jwtRefresh: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["jwtRefreshInput"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["jwtRefreshOutput"]; + }; + }; + }; + }; +} + + +// ============================================================================ +// Convenience Type Exports +// ============================================================================ + +export type echoOutput = components["schemas"]["echoOutput"] +export type echoInput = components["schemas"]["echoInput"] +export type addOutput = components["schemas"]["addOutput"] +export type addInput = components["schemas"]["addInput"] +export type whoamiOutput = components["schemas"]["whoamiOutput"] +export type httpOnlyEchoOutput = components["schemas"]["httpOnlyEchoOutput"] +export type httpOnlyEchoInput = components["schemas"]["httpOnlyEchoInput"] +export type FormSchemaField = components["schemas"]["FormSchemaField"] +export type loginSchemaOutput = components["schemas"]["loginSchemaOutput"] +export type loginValidateOutput = components["schemas"]["loginValidateOutput"] +export type loginValidateInput = components["schemas"]["loginValidateInput"] +export type loginSubmitOutput = components["schemas"]["loginSubmitOutput"] +export type loginSubmitInput = components["schemas"]["loginSubmitInput"] +export type signupSchemaOutput = components["schemas"]["signupSchemaOutput"] +export type signupValidateOutput = components["schemas"]["signupValidateOutput"] +export type signupValidateInput = components["schemas"]["signupValidateInput"] +export type signupSubmitOutput = components["schemas"]["signupSubmitOutput"] +export type signupSubmitInput = components["schemas"]["signupSubmitInput"] +export type addEmailSchemaOutput = components["schemas"]["addEmailSchemaOutput"] +export type addEmailValidateOutput = components["schemas"]["addEmailValidateOutput"] +export type addEmailValidateInput = components["schemas"]["addEmailValidateInput"] +export type staffOnlyOutput = components["schemas"]["staffOnlyOutput"] +export type superuserOnlyOutput = components["schemas"]["superuserOnlyOutput"] +export type verifiedOnlyOutput = components["schemas"]["verifiedOnlyOutput"] +export type currentUserOutput = components["schemas"]["currentUserOutput"] +export type greetOutput = components["schemas"]["greetOutput"] +export type greetInput = components["schemas"]["greetInput"] +export type multiplyOutput = components["schemas"]["multiplyOutput"] +export type multiplyInput = components["schemas"]["multiplyInput"] +export type notImplementedFnOutput = components["schemas"]["notImplementedFnOutput"] +export type buggyFnOutput = components["schemas"]["buggyFnOutput"] +export type permissionCheckFnOutput = components["schemas"]["permissionCheckFnOutput"] +export type permissionCheckFnInput = components["schemas"]["permissionCheckFnInput"] +export type wsWhoamiOutput = components["schemas"]["wsWhoamiOutput"] +export type FieldChoice = components["schemas"]["FieldChoice"] +export type FieldSchema = components["schemas"]["FieldSchema"] +export type FormMeta = components["schemas"]["FormMeta"] +export type contactSchemaOutput = components["schemas"]["contactSchemaOutput"] +export type contactSchemaInput = components["schemas"]["contactSchemaInput"] +export type FieldError = components["schemas"]["FieldError"] +export type FieldErrorList = components["schemas"]["FieldErrorList"] +export type contactValidateOutput = components["schemas"]["contactValidateOutput"] +export type contactValidateInput = components["schemas"]["contactValidateInput"] +export type contactSubmitOutput = components["schemas"]["contactSubmitOutput"] +export type itemSchemaOutput = components["schemas"]["itemSchemaOutput"] +export type itemSchemaInput = components["schemas"]["itemSchemaInput"] +export type itemValidateOutput = components["schemas"]["itemValidateOutput"] +export type itemValidateInput = components["schemas"]["itemValidateInput"] +export type itemSubmitOutput = components["schemas"]["itemSubmitOutput"] +export type FormSchema = components["schemas"]["FormSchema"] +export type itemFormsetSchemaOutput = components["schemas"]["itemFormsetSchemaOutput"] +export type itemFormsetSchemaInput = components["schemas"]["itemFormsetSchemaInput"] +export type FormValidation = components["schemas"]["FormValidation"] +export type itemFormsetValidateOutput = components["schemas"]["itemFormsetValidateOutput"] +export type itemFormsetValidateInput = components["schemas"]["itemFormsetValidateInput"] +export type itemFormsetSubmitOutput = components["schemas"]["itemFormsetSubmitOutput"] +export type itemFormsetSubmitInput = components["schemas"]["itemFormsetSubmitInput"] +export type jwtObtainOutput = components["schemas"]["jwtObtainOutput"] +export type jwtRefreshOutput = components["schemas"]["jwtRefreshOutput"] +export type jwtRefreshInput = components["schemas"]["jwtRefreshInput"] + +// ============================================================================ +// Function Registry (for reference) +// ============================================================================ + +export type Transport = 'http' | 'websocket' | 'both' + +export const DJANGO_FUNCTIONS = { + echo: { + name: 'echo', + hasInput: true, + isContext: false, + transport: 'websocket' as Transport, + }, + add: { + name: 'add', + hasInput: true, + isContext: false, + transport: 'websocket' as Transport, + }, + whoami: { + name: 'whoami', + hasInput: false, + isContext: false, + transport: 'http' as Transport, + }, + httpOnlyEcho: { + name: 'http_only_echo', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + loginSchema: { + name: 'login.schema', + hasInput: false, + isContext: false, + transport: 'http' as Transport, + }, + loginValidate: { + name: 'login.validate', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + loginSubmit: { + name: 'login.submit', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + signupSchema: { + name: 'signup.schema', + hasInput: false, + isContext: false, + transport: 'http' as Transport, + }, + signupValidate: { + name: 'signup.validate', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + signupSubmit: { + name: 'signup.submit', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + addEmailSchema: { + name: 'add_email.schema', + hasInput: false, + isContext: false, + transport: 'http' as Transport, + }, + addEmailValidate: { + name: 'add_email.validate', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + staffOnly: { + name: 'staff_only', + hasInput: false, + isContext: false, + transport: 'http' as Transport, + }, + superuserOnly: { + name: 'superuser_only', + hasInput: false, + isContext: false, + transport: 'http' as Transport, + }, + verifiedOnly: { + name: 'verified_only', + hasInput: false, + isContext: false, + transport: 'http' as Transport, + }, + currentUser: { + name: 'current_user', + hasInput: false, + isContext: global, + transport: 'http' as Transport, + }, + greet: { + name: 'greet', + hasInput: true, + isContext: local, + transport: 'http' as Transport, + }, + multiply: { + name: 'multiply', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + notImplementedFn: { + name: 'not_implemented_fn', + hasInput: false, + isContext: false, + transport: 'http' as Transport, + }, + buggyFn: { + name: 'buggy_fn', + hasInput: false, + isContext: false, + transport: 'http' as Transport, + }, + permissionCheckFn: { + name: 'permission_check_fn', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + wsWhoami: { + name: 'ws_whoami', + hasInput: false, + isContext: false, + transport: 'websocket' as Transport, + }, + contactSchema: { + name: 'contact.schema', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + contactValidate: { + name: 'contact.validate', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + contactSubmit: { + name: 'contact.submit', + hasInput: false, + isContext: false, + transport: 'http' as Transport, + }, + itemSchema: { + name: 'item.schema', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + itemValidate: { + name: 'item.validate', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + itemSubmit: { + name: 'item.submit', + hasInput: false, + isContext: false, + transport: 'http' as Transport, + }, + itemFormsetSchema: { + name: 'item.formset.schema', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + itemFormsetValidate: { + name: 'item.formset.validate', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + itemFormsetSubmit: { + name: 'item.formset.submit', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, + jwtObtain: { + name: 'jwt_obtain', + hasInput: false, + isContext: false, + transport: 'http' as Transport, + }, + jwtRefresh: { + name: 'jwt_refresh', + hasInput: true, + isContext: false, + transport: 'http' as Transport, + }, +} as const diff --git a/examples/django-react-site/harness/src/api/generated.forms.ts b/examples/django-react-site/harness/src/api/generated.forms.ts new file mode 100644 index 0000000..ed05078 --- /dev/null +++ b/examples/django-react-site/harness/src/api/generated.forms.ts @@ -0,0 +1,226 @@ +'use client' + +// AUTO-GENERATED by mizan - do not edit manually +// Regenerate with: npm run schemas + +// Typed form hooks with Zod validation. +// Zod schemas are generated from Django form field definitions. +// Client-side validation matches Django constraints (required, max_length, email, etc.) + +import { z } from 'zod' +import { + useDjangoFormCore, + useDjangoFormsetCore, + type DjangoFormState, + type DjangoFormsetState, + type FormOptions, +} from 'mizan' + +// ============================================================================ +// Zod Schemas +// ============================================================================ + +/** + * Zod schema for login form + * Generated from Django form field definitions + */ +export const LoginSchema = z.object({ +}) + +/** + * Zod schema for signup form + * Generated from Django form field definitions + */ +export const SignupSchema = z.object({ +}) + +/** + * Zod schema for add_email form + * Generated from Django form field definitions + */ +export const AddEmailSchema = z.object({ +}) + +/** + * Zod schema for contact form + * Generated from Django form field definitions + */ +export const ContactSchema = z.object({ + name: z.string().max(100), + email: z.string().email('Invalid email address').max(320), + message: z.string(), +}) + +/** + * Zod schema for item form + * Generated from Django form field definitions + */ +export const ItemSchema = z.object({ + label: z.string().max(50), + quantity: z.number().int().min(1), +}) + +// ============================================================================ +// Form Data Types (inferred from Zod schemas) +// ============================================================================ + +/** Form data type for login, inferred from Zod schema */ +export type LoginFormData = z.infer + +/** Form data type for signup, inferred from Zod schema */ +export type SignupFormData = z.infer + +/** Form data type for add_email, inferred from Zod schema */ +export type AddEmailFormData = z.infer + +/** Form data type for contact, inferred from Zod schema */ +export type ContactFormData = z.infer + +/** Form data type for item, inferred from Zod schema */ +export type ItemFormData = z.infer + +// ============================================================================ +// Form Hooks +// ============================================================================ + +/** + * Typed form hook for login + * + * Features: + * - Full TypeScript inference for form fields + * - Client-side Zod validation (instant feedback) + * - Server-side Django validation (authoritative) + */ +export function useLoginForm( + options?: FormOptions +): DjangoFormState { + return useDjangoFormCore({ + name: 'login', + zodSchema: LoginSchema, + options, + }) +} + +/** + * Typed form hook for signup + * + * Features: + * - Full TypeScript inference for form fields + * - Client-side Zod validation (instant feedback) + * - Server-side Django validation (authoritative) + */ +export function useSignupForm( + options?: FormOptions +): DjangoFormState { + return useDjangoFormCore({ + name: 'signup', + zodSchema: SignupSchema, + options, + }) +} + +/** + * Typed form hook for add_email + * + * Features: + * - Full TypeScript inference for form fields + * - Client-side Zod validation (instant feedback) + * - Server-side Django validation (authoritative) + */ +export function useAddEmailForm( + options?: FormOptions +): DjangoFormState { + return useDjangoFormCore({ + name: 'add_email', + zodSchema: AddEmailSchema, + options, + }) +} + +/** + * Typed form hook for contact + * + * Features: + * - Full TypeScript inference for form fields + * - Client-side Zod validation (instant feedback) + * - Server-side Django validation (authoritative) + */ +export function useContactForm( + options?: FormOptions +): DjangoFormState { + return useDjangoFormCore({ + name: 'contact', + zodSchema: ContactSchema, + options, + }) +} + +/** + * Typed form hook for item + * + * Features: + * - Full TypeScript inference for form fields + * - Client-side Zod validation (instant feedback) + * - Server-side Django validation (authoritative) + */ +export function useItemForm( + options?: FormOptions +): DjangoFormState { + return useDjangoFormCore({ + name: 'item', + zodSchema: ItemSchema, + options, + }) +} + +/** + * Typed formset hook for item + */ +export function useItemFormset( + initialCount?: number, + liveValidation?: boolean +): DjangoFormsetState { + return useDjangoFormsetCore({ + name: 'item', + zodSchema: ItemSchema, + initialCount, + liveValidation, + }) +} + +// ============================================================================ +// Form Registry +// ============================================================================ + +export const DJANGO_FORMS = { + login: { + name: 'login', + schema: LoginSchema, + hook: 'useLoginForm', + hasFormset: false, + }, + signup: { + name: 'signup', + schema: SignupSchema, + hook: 'useSignupForm', + hasFormset: false, + }, + addEmail: { + name: 'add_email', + schema: AddEmailSchema, + hook: 'useAddEmailForm', + hasFormset: false, + }, + contact: { + name: 'contact', + schema: ContactSchema, + hook: 'useContactForm', + hasFormset: false, + }, + item: { + name: 'item', + schema: ItemSchema, + hook: 'useItemForm', + hasFormset: true, + }, +} as const diff --git a/e2e/harness/src/api/index.ts b/examples/django-react-site/harness/src/api/index.ts similarity index 100% rename from e2e/harness/src/api/index.ts rename to examples/django-react-site/harness/src/api/index.ts diff --git a/e2e/harness/src/fixtures.tsx b/examples/django-react-site/harness/src/fixtures.tsx similarity index 100% rename from e2e/harness/src/fixtures.tsx rename to examples/django-react-site/harness/src/fixtures.tsx diff --git a/e2e/harness/src/main.tsx b/examples/django-react-site/harness/src/main.tsx similarity index 100% rename from e2e/harness/src/main.tsx rename to examples/django-react-site/harness/src/main.tsx diff --git a/examples/django-react-site/harness/test-results/.last-run.json b/examples/django-react-site/harness/test-results/.last-run.json new file mode 100644 index 0000000..5fca3f8 --- /dev/null +++ b/examples/django-react-site/harness/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "failed", + "failedTests": [] +} \ No newline at end of file diff --git a/e2e/harness/tsconfig.json b/examples/django-react-site/harness/tsconfig.json similarity index 100% rename from e2e/harness/tsconfig.json rename to examples/django-react-site/harness/tsconfig.json diff --git a/e2e/harness/vite.config.ts b/examples/django-react-site/harness/vite.config.ts similarity index 100% rename from e2e/harness/vite.config.ts rename to examples/django-react-site/harness/vite.config.ts diff --git a/playwright.config.ts b/playwright.config.ts index c73af61..2c72c6d 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from '@playwright/test' export default defineConfig({ - testDir: './e2e', + testDir: './examples/django-react-site', timeout: 15000, retries: 0, reporter: 'list',