Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
PlaygroundController
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
2 / 2
2
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __invoke
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2namespace Apie\TypescriptClientBuilder\Controllers;
3
4use Nyholm\Psr7\Response;
5use Psr\Http\Message\ResponseInterface;
6
7/**
8 * Serves a test page that runs the generated TypeScript client in the BrowserPlayground.
9 */
10class PlaygroundController
11{
12    public function __construct(private readonly string $jsBasePath = '/js/contents')
13    {
14    }
15
16    public function __invoke(): ResponseInterface
17    {
18        $base = htmlspecialchars($this->jsBasePath, ENT_QUOTES);
19        $html = <<<HTML
20<!DOCTYPE html>
21<html lang="en">
22<head>
23<meta charset="utf-8">
24<title>Apie Test Playground</title>
25<meta name="viewport" content="width=device-width, initial-scale=1">
26<script type="importmap">
27{
28  "imports": {
29    "{$base}/es6/index": "{$base}/es6/index.js",
30    "{$base}/es6/build-script": "{$base}/es6/build-script.js",
31    "{$base}/es6/transport-layer": "{$base}/es6/transport-layer.js"
32  }
33}
34</script>
35<style>
36body { font-family: system-ui, sans-serif; margin: 0; background: #f4f5f7; color: #1d2433; }
37#playground { max-width: 1200px; margin: 0 auto; padding: 1rem; }
38header { display: flex; justify-content: space-between; align-items: center; }
39.layout { display: grid; grid-template-columns: 260px 1fr; gap: 1rem; }
40.panel { background: #fff; border: 1px solid #d9dce3; border-radius: 6px; padding: 1rem; margin-bottom: 1rem; }
41.panel-head { display: flex; justify-content: space-between; align-items: center; }
42.tabs, .entity-list, .flags, .test-buttons { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .5rem; }
43.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: .75rem; }
44.grid label { display: flex; flex-direction: column; font-size: .85rem; gap: .2rem; }
45button { padding: .35rem .8rem; border: 1px solid #b8bdc9; border-radius: 4px; background: #fff; cursor: pointer; }
46button.active, button.primary { background: #2b5fd9; border-color: #2b5fd9; color: #fff; }
47input, select, textarea { padding: .3rem; border: 1px solid #b8bdc9; border-radius: 4px; }
48.muted, .empty { color: #6b7385; }
49.output, pre { background: #101624; color: #d7e0f5; padding: .75rem; border-radius: 4px; overflow: auto; white-space: pre-wrap; min-height: 3rem; }
50</style>
51</head>
52<body>
53<div id="playground"></div>
54<script type="module">
55import { apiUrl, resourceDefinition } from '{$base}/es6/Apie.js';
56import { createBrowserPlayground } from '{$base}/es6/index.js';
57
58window.playground = createBrowserPlayground(resourceDefinition, apiUrl).start('playground');
59</script>
60</body>
61</html>
62HTML;
63        return new Response(200, ['Content-Type' => 'text/html; charset=utf-8'], $html);
64    }
65}