dewdrop-sdk 0.5.2 → 0.5.3
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 +1 -1
- package/src/evaluate.js +57 -0
- package/src/vite.js +9 -6
package/package.json
CHANGED
package/src/evaluate.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// A page the dev server serves offers `dewdrop.evaluate(code)`, as a dev
|
|
2
|
+
// version's page on Dewdrop does, so one script drives the game in both.
|
|
3
|
+
// `code`, a string or a function sent as its source text called with
|
|
4
|
+
// nothing, runs at the page's top level, as the browser's console runs
|
|
5
|
+
// what is typed into it: what the game puts on `globalThis` is in reach and
|
|
6
|
+
// its modules' own bindings are not. The call resolves with the value the
|
|
7
|
+
// code came to as JSON carries it, a promise's once it settles, `undefined`
|
|
8
|
+
// for none, or rejects with an Error carrying the text of what the code
|
|
9
|
+
// threw; code, waiting calls and values past what Dewdrop takes are
|
|
10
|
+
// refused in Dewdrop's words. The plugin puts `evaluateScript` in the
|
|
11
|
+
// page's head; it is `offerEvaluate`'s own text, so the function imports
|
|
12
|
+
// nothing and is written without a `<`.
|
|
13
|
+
|
|
14
|
+
// What Dewdrop takes: the bytes of a call's code, the calls waiting at
|
|
15
|
+
// once, and the bytes of a value's JSON.
|
|
16
|
+
export const LIMITS = Object.freeze({ code: 32764, waiting: 16, value: 65530 });
|
|
17
|
+
|
|
18
|
+
// Offers `dewdrop.evaluate` on `scope`, the page's window, within `limits`.
|
|
19
|
+
// Assigned rather than defined, since the game shares the window here.
|
|
20
|
+
export function offerEvaluate(scope, limits) {
|
|
21
|
+
const run = scope.eval;
|
|
22
|
+
const encoder = new scope.TextEncoder();
|
|
23
|
+
const describe = (error) => {
|
|
24
|
+
try {
|
|
25
|
+
return error !== null && typeof error === 'object' && typeof error.stack === 'string' ? error.stack : String(error);
|
|
26
|
+
} catch {
|
|
27
|
+
return '[unprintable]';
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
let waiting = 0;
|
|
31
|
+
async function evaluate(code) {
|
|
32
|
+
if (typeof code === 'function') code = `(${code})()`;
|
|
33
|
+
else if (typeof code !== 'string') throw new TypeError('dewdrop.evaluate takes a string or a function');
|
|
34
|
+
if (waiting >= limits.waiting) throw new RangeError(`dewdrop.evaluate has ${limits.waiting} calls waiting already`);
|
|
35
|
+
const bytes = encoder.encode(code).length;
|
|
36
|
+
if (bytes > limits.code) throw new RangeError(`dewdrop.evaluate takes code of at most ${limits.code} bytes, not ${bytes}`);
|
|
37
|
+
waiting += 1;
|
|
38
|
+
let text;
|
|
39
|
+
try {
|
|
40
|
+
text = JSON.stringify(await run(code));
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new Error(describe(error));
|
|
43
|
+
} finally {
|
|
44
|
+
waiting -= 1;
|
|
45
|
+
}
|
|
46
|
+
if (text === undefined) return undefined;
|
|
47
|
+
const length = encoder.encode(text).length;
|
|
48
|
+
if (length > limits.value) throw new Error(`the value is ${length} bytes as JSON, more than the ${limits.value} an answer carries`);
|
|
49
|
+
return JSON.parse(text);
|
|
50
|
+
}
|
|
51
|
+
scope.dewdrop = Object.freeze({ evaluate });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// The script the plugin puts in a page the dev server serves.
|
|
55
|
+
export function evaluateScript() {
|
|
56
|
+
return `(${offerEvaluate})(window, ${JSON.stringify(LIMITS)});`;
|
|
57
|
+
}
|
package/src/vite.js
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
// with a backend gets a second chunk beside it, `backend.js`. In dev: the
|
|
7
7
|
// route that carries a model call to its provider with the key from the
|
|
8
8
|
// developer's environment, the host that runs the game's backend for the
|
|
9
|
-
// tabs that join it, and the
|
|
10
|
-
// for the game's saves. Every other build is
|
|
9
|
+
// tabs that join it, and the scripts that start each page's storage empty
|
|
10
|
+
// for the game's saves and offer `dewdrop.evaluate`. Every other build is
|
|
11
|
+
// untouched. The plugin
|
|
11
12
|
// imports nothing from Vite itself, so it works linked from a checkout as
|
|
12
13
|
// well as installed, on Vite 3 through 8.
|
|
13
14
|
import { existsSync, readFileSync } from 'node:fs';
|
|
@@ -15,6 +16,7 @@ import { resolve } from 'node:path';
|
|
|
15
16
|
import { ASSETS_DIR, BACKEND_FILE, ENTRY_FILE, PAGE_FILE, detectViteMajor, openPage, outputOptions } from './build.js';
|
|
16
17
|
import { createLlmMiddleware } from './dev.js';
|
|
17
18
|
import { loadKeys } from './env.js';
|
|
19
|
+
import { evaluateScript } from './evaluate.js';
|
|
18
20
|
import { freshScript } from './fresh.js';
|
|
19
21
|
import { DEV_ROUTE } from './route.js';
|
|
20
22
|
import { createBackendHost } from './serve.js';
|
|
@@ -140,14 +142,15 @@ export default function dew() {
|
|
|
140
142
|
},
|
|
141
143
|
},
|
|
142
144
|
// The page the dev server serves is told whether the game has a
|
|
143
|
-
// backend, which the client's `backend` reads before it joins,
|
|
144
|
-
//
|
|
145
|
-
//
|
|
145
|
+
// backend, which the client's `backend` reads before it joins, starts
|
|
146
|
+
// with its storage empty, as on Dewdrop, and offers `dewdrop.evaluate`,
|
|
147
|
+
// as a dev version's page does, from a script first in the page,
|
|
148
|
+
// before anything of its own.
|
|
146
149
|
// A build's page, which no dev server serves, is left alone.
|
|
147
150
|
transformIndexHtml(html) {
|
|
148
151
|
if (!serving) return undefined;
|
|
149
152
|
return {
|
|
150
|
-
html: withFirstScript(html, freshScript(game)),
|
|
153
|
+
html: withFirstScript(html, freshScript(game) + evaluateScript()),
|
|
151
154
|
tags: [{ tag: 'meta', attrs: { name: 'dew-backend', content: entry === null ? 'no' : 'yes' }, injectTo: 'head' }],
|
|
152
155
|
};
|
|
153
156
|
},
|