@cosmicdrift/kumiko-server-runtime 0.319.0 → 0.321.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,100 +0,0 @@
1
- // injectSchema is shared by the dev-server (every HTML response) and
2
- // prod-server (static-fallback index.html) paths. A bug here would be a
3
- // silent production failure: createKumikoApp wouldn't find
4
- // `window.__KUMIKO_SCHEMA__` and would mount empty. Tests pin idempotency
5
- // plus the two insertion points (before the /client.js tag or before </body>).
6
-
7
- import { describe, expect, test } from "bun:test";
8
- import { injectSchema } from "../inject-schema";
9
-
10
- const SCHEMA = '{"features":[]}';
11
- const TAG = `<script>window.__KUMIKO_SCHEMA__=${SCHEMA};</script>`;
12
-
13
- describe("injectSchema", () => {
14
- test("HTML mit /client.js-Tag: Schema-Tag wird DAVOR eingefügt", () => {
15
- const html = '<html><body><script src="/client.js" defer></script></body></html>';
16
- const out = injectSchema(html, SCHEMA);
17
- expect(out).toContain(TAG);
18
- // Schema MUST come before the client bundle — otherwise createKumikoApp()
19
- // runs before window.__KUMIKO_SCHEMA__ is set.
20
- expect(out.indexOf(TAG)).toBeLessThan(out.indexOf('<script src="/client.js"'));
21
- });
22
-
23
- test("HTML mit module-form /client.js-Tag: Schema-Tag wird DAVOR eingefügt", () => {
24
- const html = '<html><body><script type="module" src="/client.js"></script></body></html>';
25
- const out = injectSchema(html, SCHEMA);
26
- expect(out).toContain(TAG);
27
- // Dev templates use `type="module"`, not the classic form — insertion
28
- // must match this tag pattern too.
29
- expect(out.indexOf(TAG)).toBeLessThan(out.indexOf('<script type="module" src="/client.js"'));
30
- });
31
-
32
- test("HTML ohne /client.js-Tag: Schema-Tag wird vor </body> eingefügt", () => {
33
- const html = "<html><body><div id=root></div></body></html>";
34
- const out = injectSchema(html, SCHEMA);
35
- expect(out).toContain(TAG);
36
- expect(out.indexOf(TAG)).toBeLessThan(out.indexOf("</body>"));
37
- });
38
-
39
- test("HTML ohne </body>: Schema-Tag wird angehängt (defensiver Fallback)", () => {
40
- const html = "<div>fragment</div>";
41
- const out = injectSchema(html, SCHEMA);
42
- expect(out.endsWith(TAG)).toBe(true);
43
- });
44
-
45
- test("Idempotent: bei bereits injectem Schema kein zweiter Tag", () => {
46
- const html = `<html><body>${TAG}</body></html>`;
47
- const out = injectSchema(html, '{"features":[{"differentSchema":true}]}');
48
- // Original tag stays, no second tag added — the marker check otherwise
49
- // prevents stacking tags on repeated reads.
50
- expect(out).toBe(html);
51
- });
52
-
53
- test("Schema mit Komplex-Daten (entities + screens) bleibt valides JS", () => {
54
- const complex = JSON.stringify({
55
- features: [
56
- {
57
- featureName: "items",
58
- entities: { item: { fields: { title: { type: "text" } } } },
59
- screens: [{ id: "list", type: "entityList", entity: "item", columns: ["title"] }],
60
- },
61
- ],
62
- });
63
- const html = '<html><body><script src="/client.js"></script></body></html>';
64
- const out = injectSchema(html, complex);
65
- // Sanity: the injected script must be valid JS (object-literal syntax,
66
- // no HTML-reserved chars in the JSON that would break the <script>
67
- // block — JSON.stringify doesn't escape /, < etc., but the standard
68
- // chars used here are harmless).
69
- expect(out).toContain(`window.__KUMIKO_SCHEMA__=${complex}`);
70
- });
71
-
72
- test("Schema mit $-Replacement-Patterns bleibt byte-identisch (kein HTML-Splicing)", () => {
73
- // String.prototype.replace() interprets $$, $&, $`, $' specially in the
74
- // REPLACEMENT argument. The injected schema JSON string lands there — an
75
- // i18n label containing "$'" or similar would otherwise splice parts of
76
- // `html` into the script tag. A replacer-function argument sidesteps that.
77
- const dollarSchema = '{"label":"$\' tail $$ amp $& end"}';
78
- const dollarTag = `<script>window.__KUMIKO_SCHEMA__=${dollarSchema};</script>`;
79
- const html = '<html><body><script src="/client.js"></script></body></html>';
80
- const out = injectSchema(html, dollarSchema);
81
- // Insertion point is right before the /client.js tag, not the start of
82
- // the document — built via indexOf/slice, not .replace(), so the
83
- // expected value doesn't itself run through the same $-pattern footgun.
84
- const clientScriptIdx = html.indexOf('<script src="/client.js"');
85
- const expected = html.slice(0, clientScriptIdx) + dollarTag + html.slice(clientScriptIdx);
86
- expect(out).toBe(expected);
87
- });
88
-
89
- test("Schema mit $-Replacement-Patterns, Insertion vor </body>", () => {
90
- const dollarSchema = '{"label":"$\' tail $$ amp $& end"}';
91
- const dollarTag = `<script>window.__KUMIKO_SCHEMA__=${dollarSchema};</script>`;
92
- const html = "<html><body><div id=root></div></body></html>";
93
- const out = injectSchema(html, dollarSchema);
94
- // Built via slice/concat, not .replace() — the expected value must not
95
- // itself run through the same $-pattern footgun the fix removes.
96
- const bodyIdx = html.indexOf("</body>");
97
- const expected = html.slice(0, bodyIdx) + dollarTag + html.slice(bodyIdx);
98
- expect(out).toBe(expected);
99
- });
100
- });
@@ -1,33 +0,0 @@
1
- // Injiziert das Server-aufgelöste AppSchema in das HTML-Template damit
2
- // createKumikoApp() es synchron unter `window.__KUMIKO_SCHEMA__` vorfindet.
3
- // JSON ist valides JS — direkt eingebettet, der Browser parsed das
4
- // Object-Literal nativ.
5
- //
6
- // Geteilt zwischen dev-server (Schema kommt frisch aus dem laufenden
7
- // Prozess) und prod-server (Schema wird beim Boot einmal berechnet und
8
- // in die statisch ausgelieferte index.html injiziert). Beide Pfade
9
- // nutzen dieselbe Tag-Form damit `createKumikoApp` den Lookup nicht je
10
- // nach Kontext anders machen muss.
11
- //
12
- // Idempotenz: wenn das HTML schon einen __KUMIKO_SCHEMA__-Marker hat,
13
- // wird nicht doppelt injected — verhindert dass repeated index.html-
14
- // Reads (prod) oder bereits-vorbereitete templates (custom CI-builds)
15
- // stacking-Tags produzieren.
16
-
17
- // `<` als < serialisieren: verhindert `</script>`-Breakout aus dem
18
- // RAWTEXT-Block, JSON bleibt valides JS.
19
- function scriptSafeJsonHtml(json: string): string {
20
- return json.replace(/</g, "\\u003c");
21
- }
22
-
23
- const CLIENT_SCRIPT_TAG_RE = /<script\b[^>]*\ssrc="\/client\.js"/;
24
-
25
- export function injectSchema(html: string, schemaJson: string): string {
26
- if (html.includes("__KUMIKO_SCHEMA__")) return html;
27
- const tag = `<script>window.__KUMIKO_SCHEMA__=${scriptSafeJsonHtml(schemaJson)};</script>`;
28
- const clientScriptMatch = html.match(CLIENT_SCRIPT_TAG_RE);
29
- if (clientScriptMatch) {
30
- return html.replace(CLIENT_SCRIPT_TAG_RE, (m) => `${tag}${m}`);
31
- }
32
- return html.includes("</body>") ? html.replace("</body>", () => `${tag}</body>`) : html + tag;
33
- }