Skip to content

JS Runtime Package #205

@tumidi

Description

@tumidi

Die Logik des Attempt-Iframe lebt aktuell in einem jinja2-Template, was historisch gewachsen, aber eigentlich suboptimal ist.

Diese Nachteile sehe ich im aktuellen Ansatz:

  • "Template-Abuse": Jinja wird verwendet, um JS-Code zu generieren.
  • Type safety: Inline JS im Template ist nicht statisch analysierbar.
  • Testability: Nur schwer oder gar nicht zu testen.
  • Global Pollution: attempt wird als globale Variable gesetzt.
  • Framework boundary: Der Inhalt des iframe's sollte als separate (Mini-)Applikation angesehen werden.
  • Unnötige "Cross-project" Code-Duplizierung zwischen SDK und Moodle Plugin.

Die Idee wäre, eine "Mini-Lib", geschrieben in TypeScript zu erstellen, die vom SDK (und Moodle-Plugin?) verwendet wird.

  • Verantwortlichkeiten klar trennen:
    • Host application (Vue SPA, Moodle, ...)
      • spricht via postMessage mit dem iframe
      • greift niemals direkt in den iframe DOM.
    • Question iframe
      • unabhängige "Mini-Applikation"
      • exposed nötige JS-API (Attempt-Objekt, Formdaten, resize, theme switch, etc.)
      • Führt zusätzlichen JS-Code vom QPy-Package im iframe aus.

Alles was Logik/Verhalten ist, wandert außerhalb des iframe in eine "Mini-Lib". Die wird einmal gebundled und als static asset serviert.

Das Jinja2-Template wird stark vereinfacht und enthält ausschließlich Daten, etwa

{% extends "page.html.jinja2" %}
{% block head %}
    {{ super() }}
    <base target="_blank">
    <link rel="stylesheet" href="{{ static_url('question-iframe.css') }}">
    {% for url in stylesheet_urls %}<link rel="stylesheet" href="{{ url|safe }}">{% endfor %}
{% endblock head %}

{% block content %}
    <div id="qpy-root">
        {# Question formulation, feedback, etc. (wie bisher) #}
    </div>

    {# Data-only #}
    <script type="application/json" id="qpy-runtime-data">
        {
            "displayOptions": {{ display_options|tojson }},
            "javascriptCalls": {{ javascript_calls|tojson }},
        }
    </script>

    <script type="importmap">
        { "imports": {{ import_map|tojson }} }
    </script>

    {# Runtime bundle #}
    <script type="module" src="/static/iframe-runtime.js"></script>
{% endblock content %}

@questionpy/iframe-runtime Package

Skizze:

# Python SDK / Moodle plugin
npm install @questionpy/iframe-runtime
/questionpy-iframe-runtime
├─ src
│   ├─ index.ts       # Public API (bootstraps the iframe)
│   ├─ Attempt.ts     # Attempt class
│   ├─ events.ts      # postMessage protocol
│   ├─ resize.ts      # ResizeObserver logic
│   ├─ theme.ts       # Color mode/custom theme logic
│   ├─ ...
│   └─ types.d.ts     # Shared type definitions
├─ package.json
├─ tsconfig.json
└─ rollup.config.ts

@questionpy/iframe-runtime lebt entweder innerhalb des SDKs, oder wenn das Moodle-Plugin auch diese Lib verwenden soll (m.M.n. die präferierte Variante), dann könnte @questionpy/iframe-runtime evtl. auch in ein eigenes Repository wandern.

Integration mit Host-Apps

  • Host-Apps können mit Hilfe des Templates/Render-Logik Einfluss auf die DOM-Struktur des iframe-Inhalts nehmen und bei Bedarf auch custom CSS injizieren.
  • Je nach Host-App beinhaltet das Plugin (oder SDK in diesem Fall) JavaScript-Adapter-Code, der mit dem iframe spricht.
  • Rollup baut verschiedene (ESM-, AMD-, iife-Module, ...), so dass die LMS und ihre Modulsystem-Constraints abgedeckt werden.

Integration mit Question-Package-Code

Die Lib bietet eine einheitliche API, um Paketen ein Interface zum Attempt-Objekt (und zukünftig evtl. noch anderen Dingen?) zu bieten. Sollte einen Promise zurückliefern, so dass sichergestellt ist, dass der Code komplett initialisiert und bereit ist, etwa

import { getAttempt } from '@questionpy/iframe-runtime'

const attempt = await getAttempt()

Vorteile

  • Type Safety: Volle Coverage für die iframe-Runtime-Logik
  • Entkoppelte Architektur: Klare Trennung zwischen
    • Host-App (SDK bzw. LMS)
    • Iframe-Runtime (generisches TypeScript)
    • Question Package (HTML/JS/CSS)
  • Code wiederverwendbar zwischen SDK und LMS-Plugins
  • Testability: Runtime-Logik kann komplett "ge-unit-tested" werden
  • Klar definierte API zwischen
    • Host und iframe-Runtime (Message passing)
    • Paket- und iframe-Runtime-Code
    • (beide APIs könnten bei Bedarf versioniert werden)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions