@cosmicdrift/kumiko-server-runtime 0.194.0 → 0.196.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-server-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.196.0",
|
|
4
4
|
"description": "Production server-boot runtime for Kumiko apps: connections, schema-drift-gate, seeds, lifecycle, graceful shutdown. Symmetric to kumiko-dev-server's runDevApp, without dev/scaffold/codegen tooling.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
}
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
80
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
79
|
+
"@cosmicdrift/kumiko-bundled-features": "0.196.0",
|
|
80
|
+
"@cosmicdrift/kumiko-framework": "0.196.0",
|
|
81
81
|
"temporal-polyfill": "^0.3.2"
|
|
82
82
|
},
|
|
83
83
|
"publishConfig": {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// injectSchema
|
|
2
|
-
// server (static-fallback index.html)
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
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
6
|
|
|
7
7
|
import { describe, expect, test } from "bun:test";
|
|
8
8
|
import { injectSchema } from "../inject-schema";
|
|
@@ -15,8 +15,8 @@ describe("injectSchema", () => {
|
|
|
15
15
|
const html = '<html><body><script src="/client.js" defer></script></body></html>';
|
|
16
16
|
const out = injectSchema(html, SCHEMA);
|
|
17
17
|
expect(out).toContain(TAG);
|
|
18
|
-
// Schema
|
|
19
|
-
//
|
|
18
|
+
// Schema MUST come before the client bundle — otherwise createKumikoApp()
|
|
19
|
+
// runs before window.__KUMIKO_SCHEMA__ is set.
|
|
20
20
|
expect(out.indexOf(TAG)).toBeLessThan(out.indexOf('<script src="/client.js"'));
|
|
21
21
|
});
|
|
22
22
|
|
|
@@ -45,8 +45,8 @@ describe("injectSchema", () => {
|
|
|
45
45
|
test("Idempotent: bei bereits injectem Schema kein zweiter Tag", () => {
|
|
46
46
|
const html = `<html><body>${TAG}</body></html>`;
|
|
47
47
|
const out = injectSchema(html, '{"features":[{"differentSchema":true}]}');
|
|
48
|
-
// Original
|
|
49
|
-
//
|
|
48
|
+
// Original tag stays, no second tag added — the marker check otherwise
|
|
49
|
+
// prevents stacking tags on repeated reads.
|
|
50
50
|
expect(out).toBe(html);
|
|
51
51
|
});
|
|
52
52
|
|
|
@@ -62,10 +62,39 @@ describe("injectSchema", () => {
|
|
|
62
62
|
});
|
|
63
63
|
const html = '<html><body><script src="/client.js"></script></body></html>';
|
|
64
64
|
const out = injectSchema(html, complex);
|
|
65
|
-
// Sanity:
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
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
69
|
expect(out).toContain(`window.__KUMIKO_SCHEMA__=${complex}`);
|
|
70
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
|
+
});
|
|
71
100
|
});
|
package/src/inject-schema.ts
CHANGED
|
@@ -27,7 +27,7 @@ export function injectSchema(html: string, schemaJson: string): string {
|
|
|
27
27
|
const tag = `<script>window.__KUMIKO_SCHEMA__=${scriptSafeJsonHtml(schemaJson)};</script>`;
|
|
28
28
|
const clientScriptMatch = html.match(CLIENT_SCRIPT_TAG_RE);
|
|
29
29
|
if (clientScriptMatch) {
|
|
30
|
-
return html.replace(
|
|
30
|
+
return html.replace(CLIENT_SCRIPT_TAG_RE, (m) => `${tag}${m}`);
|
|
31
31
|
}
|
|
32
|
-
return html.includes("</body>") ? html.replace("</body>", `${tag}</body>`) : html + tag;
|
|
32
|
+
return html.includes("</body>") ? html.replace("</body>", () => `${tag}</body>`) : html + tag;
|
|
33
33
|
}
|