@monimesl/dot-simulator 0.1.0-alpha.2
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/README.md +20 -0
- package/dist/client/service-bridge.js +123 -0
- package/dist/client/service-bridge.js.map +1 -0
- package/dist/client/simulator.js +690 -0
- package/dist/client/simulator.js.map +1 -0
- package/dist/client/webview-bridge.js +32 -0
- package/dist/client/webview-bridge.js.map +1 -0
- package/dist/node/index.d.ts +93 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +493 -0
- package/dist/node/index.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# @monimesl/dot-simulator
|
|
2
|
+
|
|
3
|
+
The browser host for Dot mini apps, used during development. It runs the service bundle in a
|
|
4
|
+
Worker, shows the webview in a phone-sized frame, relays messages between them, owns the
|
|
5
|
+
navigation stack, and logs every message. `dotctl dev` starts it. See [docs/hosts.md](../../../docs/hosts.md).
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { startSimulator } from '@monimesl/dot-simulator';
|
|
9
|
+
|
|
10
|
+
const simulator = await startSimulator({ dotDir: 'my-dot/dist', name: 'My dot', port: 3680 });
|
|
11
|
+
simulator.reload(); // restart the dot after a rebuild
|
|
12
|
+
simulator.error('view.dml:3:7: ...'); // show a build error over the device
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- `src/server.ts`: serves the simulator page, the dot's bundles and the reload event stream. It
|
|
16
|
+
injects the bridge into the webview's `index.html`.
|
|
17
|
+
- `src/host.ts`: `startHost()`, what every host does: relay messages, own the navigation stack
|
|
18
|
+
(`NavigationStack` from core) and announce it with `stack`.
|
|
19
|
+
- `src/client/`: the browser scripts. `simulator.js` is the page, and `webview-bridge.js` and
|
|
20
|
+
`service-bridge.js` install `__hostTransport` in each layer.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
//#region ../../core/dist/index.js
|
|
3
|
+
function defineComponent(manifest) {
|
|
4
|
+
return {
|
|
5
|
+
props: {},
|
|
6
|
+
events: [],
|
|
7
|
+
...manifest
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function componentRegistry(manifests) {
|
|
11
|
+
const registry = {};
|
|
12
|
+
for (const manifest of manifests) {
|
|
13
|
+
if (manifest.name in registry) throw new Error(`Component <${manifest.name}> is defined twice`);
|
|
14
|
+
registry[manifest.name] = manifest;
|
|
15
|
+
}
|
|
16
|
+
return registry;
|
|
17
|
+
}
|
|
18
|
+
const builtinComponents = {
|
|
19
|
+
Button: defineComponent({
|
|
20
|
+
name: "Button",
|
|
21
|
+
props: {
|
|
22
|
+
variant: ["primary", "secondary"],
|
|
23
|
+
disabled: "boolean"
|
|
24
|
+
},
|
|
25
|
+
events: ["click"],
|
|
26
|
+
children: "text"
|
|
27
|
+
}),
|
|
28
|
+
Image: defineComponent({
|
|
29
|
+
name: "Image",
|
|
30
|
+
props: {
|
|
31
|
+
src: "string",
|
|
32
|
+
alt: "string",
|
|
33
|
+
width: "number",
|
|
34
|
+
height: "number"
|
|
35
|
+
},
|
|
36
|
+
events: ["load", "error"],
|
|
37
|
+
children: "none"
|
|
38
|
+
}),
|
|
39
|
+
Input: defineComponent({
|
|
40
|
+
name: "Input",
|
|
41
|
+
props: {
|
|
42
|
+
value: "string",
|
|
43
|
+
placeholder: "string",
|
|
44
|
+
label: "string",
|
|
45
|
+
type: [
|
|
46
|
+
"text",
|
|
47
|
+
"number",
|
|
48
|
+
"email",
|
|
49
|
+
"phone",
|
|
50
|
+
"password"
|
|
51
|
+
],
|
|
52
|
+
maxLength: "number",
|
|
53
|
+
disabled: "boolean"
|
|
54
|
+
},
|
|
55
|
+
events: ["change", "submit"],
|
|
56
|
+
children: "none"
|
|
57
|
+
}),
|
|
58
|
+
Text: defineComponent({
|
|
59
|
+
name: "Text",
|
|
60
|
+
props: { variant: [
|
|
61
|
+
"title",
|
|
62
|
+
"body",
|
|
63
|
+
"caption"
|
|
64
|
+
] },
|
|
65
|
+
children: "text",
|
|
66
|
+
inline: true
|
|
67
|
+
}),
|
|
68
|
+
View: defineComponent({
|
|
69
|
+
name: "View",
|
|
70
|
+
props: {
|
|
71
|
+
direction: ["column", "row"],
|
|
72
|
+
gap: [
|
|
73
|
+
"none",
|
|
74
|
+
"small",
|
|
75
|
+
"medium",
|
|
76
|
+
"large"
|
|
77
|
+
],
|
|
78
|
+
align: [
|
|
79
|
+
"stretch",
|
|
80
|
+
"start",
|
|
81
|
+
"center",
|
|
82
|
+
"end"
|
|
83
|
+
],
|
|
84
|
+
variant: ["plain", "card"]
|
|
85
|
+
},
|
|
86
|
+
events: ["tap"],
|
|
87
|
+
children: "elements"
|
|
88
|
+
})
|
|
89
|
+
};
|
|
90
|
+
componentRegistry(Object.values(builtinComponents));
|
|
91
|
+
function createAdaptorTransport(adapter) {
|
|
92
|
+
return {
|
|
93
|
+
post: (message) => adapter.postMessage(message),
|
|
94
|
+
listen(listener) {
|
|
95
|
+
const onMessage = (event) => listener(event.data);
|
|
96
|
+
adapter.addEventListener("message", onMessage);
|
|
97
|
+
return () => adapter.removeEventListener("message", onMessage);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const SERVICE_URL = `/dot/logic/dot.js`;
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/client/wire.ts
|
|
104
|
+
function isWire(data) {
|
|
105
|
+
return typeof data?.dot === "string";
|
|
106
|
+
}
|
|
107
|
+
function wireTransport(channel) {
|
|
108
|
+
return {
|
|
109
|
+
post: (message) => channel.post({ dot: JSON.stringify(message) }),
|
|
110
|
+
listen: (listener) => channel.listen((data) => {
|
|
111
|
+
if (isWire(data)) listener(JSON.parse(data.dot));
|
|
112
|
+
})
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/client/service-bridge.ts
|
|
117
|
+
const scope = globalThis;
|
|
118
|
+
globalThis.__hostTransport = wireTransport(createAdaptorTransport(scope));
|
|
119
|
+
scope.importScripts(SERVICE_URL);
|
|
120
|
+
//#endregion
|
|
121
|
+
})();
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=service-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-bridge.js","names":[],"sources":["../../../../core/dist/index.js","../../src/paths.ts","../../src/client/wire.ts","../../src/client/service-bridge.ts"],"sourcesContent":["//#region src/view/component.ts\nfunction defineComponent(manifest) {\n\treturn {\n\t\tprops: {},\n\t\tevents: [],\n\t\t...manifest\n\t};\n}\nfunction eventProp(event) {\n\treturn `on${event.charAt(0).toUpperCase()}${event.slice(1)}`;\n}\nfunction isEventProp(name) {\n\tconst letterAfterOn = name.charAt(2);\n\treturn name.startsWith(\"on\") && letterAfterOn >= \"A\" && letterAfterOn <= \"Z\";\n}\nfunction eventName(prop) {\n\tconst name = prop.slice(2);\n\treturn name.charAt(0).toLowerCase() + name.slice(1);\n}\nfunction componentRegistry(manifests) {\n\tconst registry = {};\n\tfor (const manifest of manifests) {\n\t\tif (manifest.name in registry) throw new Error(`Component <${manifest.name}> is defined twice`);\n\t\tregistry[manifest.name] = manifest;\n\t}\n\treturn registry;\n}\n//#endregion\n//#region src/view/values.ts\nfunction toText(value) {\n\tswitch (typeof value) {\n\t\tcase \"string\": return value;\n\t\tcase \"number\":\n\t\tcase \"boolean\":\n\t\tcase \"bigint\": return String(value);\n\t\tcase \"object\": return value === null ? \"\" : JSON.stringify(value);\n\t\tdefault: return \"\";\n\t}\n}\nfunction toList(value) {\n\tif (Array.isArray(value)) return value;\n\tif (typeof value === \"object\" && value !== null && Symbol.iterator in value) return Array.from(value);\n\treturn [];\n}\n//#endregion\n//#region src/source/location.ts\nfunction createLocator(source) {\n\tconst lineStarts = [0];\n\tfor (let index = 0; index < source.length; index++) if (source[index] === \"\\n\") lineStarts.push(index + 1);\n\treturn (offset) => {\n\t\tlet low = 0;\n\t\tlet high = lineStarts.length - 1;\n\t\twhile (low < high) {\n\t\t\tconst middle = Math.floor((low + high + 1) / 2);\n\t\t\tif (lineStarts[middle] <= offset) low = middle;\n\t\t\telse high = middle - 1;\n\t\t}\n\t\treturn {\n\t\t\tline: low + 1,\n\t\t\tcolumn: offset - lineStarts[low] + 1,\n\t\t\toffset\n\t\t};\n\t};\n}\nfunction locationAt(source, offset) {\n\treturn createLocator(source)(offset);\n}\nfunction locatedMessage(message, file, location) {\n\tconst where = [\n\t\tfile,\n\t\tlocation?.line,\n\t\tlocation?.column\n\t].filter((part) => part !== void 0).join(\":\");\n\treturn where ? `${where}: ${message}` : message;\n}\n//#endregion\n//#region src/source/error.ts\nvar SourceError = class extends Error {\n\treason;\n\tfile;\n\tloc;\n\tconstructor(reason, file, location) {\n\t\tsuper(locatedMessage(reason, file, location));\n\t\tthis.reason = reason;\n\t\tthis.file = file;\n\t\tthis.loc = location;\n\t}\n};\n//#endregion\n//#region src/view/components/button.ts\nconst Button = defineComponent({\n\tname: \"Button\",\n\tprops: {\n\t\tvariant: [\"primary\", \"secondary\"],\n\t\tdisabled: \"boolean\"\n\t},\n\tevents: [\"click\"],\n\tchildren: \"text\"\n});\n//#endregion\n//#region src/view/components/image.ts\nconst Image = defineComponent({\n\tname: \"Image\",\n\tprops: {\n\t\tsrc: \"string\",\n\t\talt: \"string\",\n\t\twidth: \"number\",\n\t\theight: \"number\"\n\t},\n\tevents: [\"load\", \"error\"],\n\tchildren: \"none\"\n});\n//#endregion\n//#region src/view/components/input.ts\nconst Input = defineComponent({\n\tname: \"Input\",\n\tprops: {\n\t\tvalue: \"string\",\n\t\tplaceholder: \"string\",\n\t\tlabel: \"string\",\n\t\ttype: [\n\t\t\t\"text\",\n\t\t\t\"number\",\n\t\t\t\"email\",\n\t\t\t\"phone\",\n\t\t\t\"password\"\n\t\t],\n\t\tmaxLength: \"number\",\n\t\tdisabled: \"boolean\"\n\t},\n\tevents: [\"change\", \"submit\"],\n\tchildren: \"none\"\n});\n//#endregion\n//#region src/view/components/text.ts\nconst Text = defineComponent({\n\tname: \"Text\",\n\tprops: { variant: [\n\t\t\"title\",\n\t\t\"body\",\n\t\t\"caption\"\n\t] },\n\tchildren: \"text\",\n\tinline: true\n});\n//#endregion\n//#region src/view/components/view.ts\nconst View = defineComponent({\n\tname: \"View\",\n\tprops: {\n\t\tdirection: [\"column\", \"row\"],\n\t\tgap: [\n\t\t\t\"none\",\n\t\t\t\"small\",\n\t\t\t\"medium\",\n\t\t\t\"large\"\n\t\t],\n\t\talign: [\n\t\t\t\"stretch\",\n\t\t\t\"start\",\n\t\t\t\"center\",\n\t\t\t\"end\"\n\t\t],\n\t\tvariant: [\"plain\", \"card\"]\n\t},\n\tevents: [\"tap\"],\n\tchildren: \"elements\"\n});\n//#endregion\n//#region src/view/components/index.ts\nconst builtinComponents = {\n\tButton,\n\tImage,\n\tInput,\n\tText,\n\tView\n};\nconst builtinRegistry = componentRegistry(Object.values(builtinComponents));\n//#endregion\n//#region src/event/emitter.ts\nvar EventEmitter = class {\n\t#listeners = /* @__PURE__ */ new Map();\n\ton(name, listener) {\n\t\tconst listeners = this.#listeners.get(name) ?? /* @__PURE__ */ new Set();\n\t\tthis.#listeners.set(name, listeners);\n\t\tlisteners.add(listener);\n\t\treturn () => listeners.delete(listener);\n\t}\n\temit(name, ...args) {\n\t\tthis.#listeners.get(name)?.forEach((listener) => listener(...args));\n\t}\n};\n//#endregion\n//#region src/event/core.ts\nfunction createEvent(type, detail = {}) {\n\treturn {\n\t\ttype,\n\t\tdetail\n\t};\n}\n//#endregion\n//#region src/host/host.ts\nfunction hostTransport() {\n\tconst transport = globalThis.__hostTransport;\n\tif (!transport) throw new Error(\"No Dot host found: expected `globalThis.__hostTransport` to be available at runtime. Dots run inside the Dash App, or a simulator via `dotctl dev`.\");\n\treturn transport;\n}\n//#endregion\n//#region src/host/context.ts\nconst DEFAULT_SYSTEM_CONTEXT = {\n\tscheme: \"light\",\n\tappState: \"active\"\n};\n//#endregion\n//#region src/host/calls.ts\nvar HostError = class extends Error {\n\tname = \"HostError\";\n\tcode;\n\tconstructor(code, message) {\n\t\tsuper(message);\n\t\tthis.code = code;\n\t}\n};\nlet connectedCaller = null;\nfunction connectHost(caller) {\n\tconnectedCaller = caller;\n}\nfunction callHost(method, params, options = {}) {\n\tif (!connectedCaller) {\n\t\tconst error = new HostError(\"unsupported\", `No host to run \"${method}\": host calls run in the service`);\n\t\treturn createHostRequest(Promise.reject(error));\n\t}\n\treturn connectedCaller(method, params, options);\n}\nfunction createHostRequest(promise, cancel = () => {}) {\n\tconst request = promise;\n\trequest.cancel = cancel;\n\treturn request;\n}\n//#endregion\n//#region src/host/permissions.ts\nconst PERMISSIONS = [\"account\"];\nfunction isPermission(value) {\n\treturn PERMISSIONS.includes(value);\n}\n//#endregion\n//#region src/version/semantic-version.ts\nconst SEMVER_PATTERN = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$/;\nfunction isSemanticVersion(value) {\n\treturn typeof value === \"string\" && SEMVER_PATTERN.test(value);\n}\nfunction majorVersion(version) {\n\treturn parseVersion(version).major;\n}\nfunction isVersionAtLeast(version, minimum) {\n\treturn compareVersions(version, minimum) >= 0;\n}\nfunction compareVersions(left, right) {\n\tconst leftVersion = parseVersion(left);\n\tconst rightVersion = parseVersion(right);\n\tconst differences = [\n\t\tleftVersion.major - rightVersion.major,\n\t\tleftVersion.minor - rightVersion.minor,\n\t\tleftVersion.patch - rightVersion.patch\n\t];\n\tfor (const difference of differences) if (difference !== 0) return difference;\n\treturn comparePrereleases(leftVersion.prerelease, rightVersion.prerelease);\n}\nfunction parseVersion(version) {\n\tconst match = SEMVER_PATTERN.exec(version);\n\tif (!match) throw new Error(`\"${version}\" is not a semantic version`);\n\tconst [, major, minor, patch, prerelease] = match;\n\treturn {\n\t\tmajor: Number(major),\n\t\tminor: Number(minor),\n\t\tpatch: Number(patch),\n\t\tprerelease: prerelease === void 0 ? [] : prerelease.split(\".\")\n\t};\n}\nfunction comparePrereleases(left, right) {\n\tif (left.length === 0 || right.length === 0) return right.length - left.length;\n\tconst shared = Math.min(left.length, right.length);\n\tfor (let index = 0; index < shared; index++) {\n\t\tconst order = compareIdentifiers(left[index], right[index]);\n\t\tif (order !== 0) return order;\n\t}\n\treturn left.length - right.length;\n}\nfunction compareIdentifiers(left, right) {\n\tconst leftIsNumber = /^\\d+$/.test(left);\n\tconst rightIsNumber = /^\\d+$/.test(right);\n\tif (leftIsNumber && rightIsNumber) return Number(left) - Number(right);\n\tif (leftIsNumber) return -1;\n\tif (rightIsNumber) return 1;\n\tif (left < right) return -1;\n\tif (left > right) return 1;\n\treturn 0;\n}\n//#endregion\n//#region src/host/react.ts\nconst REACT_HOST_PATH = \"/__dot/react/\";\nconst REACT_HOST_MODULES = {\n\treact: \"react.js\",\n\t\"react/jsx-runtime\": \"react-jsx-runtime.js\",\n\t\"react-dom/client\": \"react-dom-client.js\"\n};\nfunction reactHostUrl(dotReactVersion, file) {\n\treturn `${REACT_HOST_PATH}${majorVersion(dotReactVersion)}/${file}`;\n}\nfunction isReactCompatible(hostVersion, dotVersion) {\n\treturn majorVersion(hostVersion) === majorVersion(dotVersion) && isVersionAtLeast(hostVersion, dotVersion);\n}\nfunction newestReactOfMajor(versions, major) {\n\treturn versions.filter((version) => majorVersion(version) === major).sort(compareVersions).at(-1);\n}\n//#endregion\n//#region src/protocol/transport.ts\nfunction createAdaptorTransport(adapter) {\n\treturn {\n\t\tpost: (message) => adapter.postMessage(message),\n\t\tlisten(listener) {\n\t\t\tconst onMessage = (event) => listener(event.data);\n\t\t\tadapter.addEventListener(\"message\", onMessage);\n\t\t\treturn () => adapter.removeEventListener(\"message\", onMessage);\n\t\t}\n\t};\n}\nfunction createTransportPair() {\n\tconst firstListeners = /* @__PURE__ */ new Set();\n\tconst secondListeners = /* @__PURE__ */ new Set();\n\tconst createSide = (ownListeners, otherListeners) => ({\n\t\tpost: (message) => {\n\t\t\tconst copy = JSON.parse(JSON.stringify(message));\n\t\t\totherListeners.forEach((listener) => listener(copy));\n\t\t},\n\t\tlisten(listener) {\n\t\t\townListeners.add(listener);\n\t\t\treturn () => ownListeners.delete(listener);\n\t\t}\n\t});\n\treturn [createSide(firstListeners, secondListeners), createSide(secondListeners, firstListeners)];\n}\nconst PAGE_HOOKS = Object.keys({\n\tonLoad: true,\n\tonShow: true,\n\tonHide: true,\n\tonUnload: true\n});\n//#endregion\n//#region src/page/navigation.ts\nconst MAX_STACK_DEPTH = 8;\nconst PAGE_ID = /^[A-Za-z0-9_-]+(?:\\/[A-Za-z0-9_-]+)*$/;\nfunction isPageId(value) {\n\treturn PAGE_ID.test(value);\n}\nvar NavigationStack = class {\n\tlimit;\n\t#pages = [];\n\t#nextInstanceId = 1;\n\tconstructor(limit = 8) {\n\t\tthis.limit = limit;\n\t}\n\tget pages() {\n\t\treturn this.#pages;\n\t}\n\tget top() {\n\t\treturn this.#pages.at(-1);\n\t}\n\t/**\n\t* Applies a navigation, or refuses it and returns false: a push onto a full stack, or going back\n\t* from the first page (which the host handles itself, e.g. by closing the dot).\n\t*/\n\tapply(navigation) {\n\t\tswitch (navigation.action) {\n\t\t\tcase \"push\":\n\t\t\t\tif (this.#pages.length >= this.limit) return false;\n\t\t\t\tbreak;\n\t\t\tcase \"replace\":\n\t\t\t\tthis.#pages.pop();\n\t\t\t\tbreak;\n\t\t\tcase \"reset\":\n\t\t\t\tthis.#pages = [];\n\t\t\t\tbreak;\n\t\t\tcase \"back\": {\n\t\t\t\tconst count = Math.min(navigation.delta, this.#pages.length - 1);\n\t\t\t\tif (count < 1) return false;\n\t\t\t\tthis.#pages = this.#pages.slice(0, -count);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tconst { page, options } = navigation;\n\t\tthis.#pages = [...this.#pages, {\n\t\t\tinstanceId: this.#nextInstanceId++,\n\t\t\tpage,\n\t\t\toptions\n\t\t}];\n\t\treturn true;\n\t}\n};\n//#endregion\n//#region src/page/state.ts\nfunction applyChanges(state, changes) {\n\tconst next = { ...state };\n\tfor (const [key, value] of Object.entries(changes)) if (value !== void 0) next[key] = value;\n\treturn next;\n}\n//#endregion\n//#region src/messageChannel.ts\nvar MessageChannel = class {\n\t#transport;\n\t#handlers = /* @__PURE__ */ new Map();\n\tconstructor(transport) {\n\t\tthis.#transport = transport;\n\t}\n\tsend(type, payload) {\n\t\tthis.#transport.post({\n\t\t\ttype,\n\t\t\tpayload\n\t\t});\n\t}\n\thandle(type, handler) {\n\t\tconst previous = this.#handlers.get(type);\n\t\tthis.#handlers.set(type, handler);\n\t\treturn previous;\n\t}\n\tlisten(onError) {\n\t\treturn this.#transport.listen((rawMessage) => {\n\t\t\tconst message = rawMessage;\n\t\t\tconst handler = this.#handlers.get(message.type);\n\t\t\tif (!handler) {\n\t\t\t\tonError(/* @__PURE__ */ new Error(`No handler registered for message \"${message.type}\"`), message);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\thandler(message.payload);\n\t\t\t} catch (error) {\n\t\t\t\tonError(error, message);\n\t\t\t}\n\t\t});\n\t}\n};\n//#endregion\nexport { Button, DEFAULT_SYSTEM_CONTEXT, EventEmitter, HostError, Image, Input, MAX_STACK_DEPTH, MessageChannel, NavigationStack, PAGE_HOOKS, PERMISSIONS, REACT_HOST_MODULES, REACT_HOST_PATH, SEMVER_PATTERN, SourceError, Text, View, applyChanges, builtinComponents, builtinRegistry, callHost, compareVersions, componentRegistry, connectHost, createAdaptorTransport, createEvent, createHostRequest, createLocator, createTransportPair, defineComponent, eventName, eventProp, hostTransport, isEventProp, isPageId, isPermission, isReactCompatible, isSemanticVersion, isVersionAtLeast, locatedMessage, locationAt, majorVersion, newestReactOfMajor, reactHostUrl, toList, toText };\n\n//# sourceMappingURL=index.js.map","export const CLIENT_PATH = '/__dot/';\nexport const EVENTS_PATH = `${CLIENT_PATH}events`;\n\nexport const DOT_PATH = '/dot/';\nexport const WEBVIEW_URL = `${DOT_PATH}ui/index.html`;\nexport const SERVICE_URL = `${DOT_PATH}logic/dot.js`;\nexport const MANIFEST_URL = `${DOT_PATH}manifest.json`;\n","import type { Transport } from '@monimesl/dot-core';\n\n// Native bridges only carry strings, so JSON-encoding here makes state that a device could not\n// carry fail in the simulator too.\ninterface Wire {\n readonly dot: string;\n}\n\nfunction isWire(data: unknown): data is Wire {\n return typeof (data as Partial<Wire> | null)?.dot === 'string';\n}\n\nexport function wireTransport(channel: Transport): Transport {\n return {\n post: (message) => channel.post({ dot: JSON.stringify(message) } satisfies Wire),\n listen: (listener) =>\n channel.listen((data) => {\n if (isWire(data)) {\n listener(JSON.parse(data.dot));\n }\n }),\n };\n}\n\n// The simulator page and the webview's iframe, each accepting messages only from the other.\nexport function windowChannel(other: () => Window | null): Transport {\n return {\n post: (message) => other()?.postMessage(message, location.origin),\n listen(listener) {\n const onMessage = (event: MessageEvent) => {\n if (event.source === other()) {\n listener(event.data);\n }\n };\n window.addEventListener('message', onMessage);\n return () => window.removeEventListener('message', onMessage);\n },\n };\n}\n","import type { TransportAdapter } from '@monimesl/dot-core';\nimport { createAdaptorTransport } from '@monimesl/dot-core';\n\nimport { SERVICE_URL } from '../paths';\nimport { wireTransport } from './wire';\n\ninterface WorkerScope extends TransportAdapter {\n importScripts(...urls: string[]): void;\n}\n\nconst scope = globalThis as unknown as WorkerScope;\n\nglobalThis.__hostTransport = wireTransport(createAdaptorTransport(scope));\n\nscope.importScripts(SERVICE_URL);\n"],"mappings":";;CACA,SAAS,gBAAgB,UAAU;EAClC,OAAO;GACN,OAAO,CAAC;GACR,QAAQ,CAAC;GACT,GAAG;EACJ;CACD;CAYA,SAAS,kBAAkB,WAAW;EACrC,MAAM,WAAW,CAAC;EAClB,KAAK,MAAM,YAAY,WAAW;GACjC,IAAI,SAAS,QAAQ,UAAU,MAAM,IAAI,MAAM,cAAc,SAAS,KAAK,mBAAmB;GAC9F,SAAS,SAAS,QAAQ;EAC3B;EACA,OAAO;CACR;CAgJA,MAAM,oBAAoB;EACzB,QAjFc,gBAAgB;GAC9B,MAAM;GACN,OAAO;IACN,SAAS,CAAC,WAAW,WAAW;IAChC,UAAU;GACX;GACA,QAAQ,CAAC,OAAO;GAChB,UAAU;EACX,CAyEC;EACA,OAvEa,gBAAgB;GAC7B,MAAM;GACN,OAAO;IACN,KAAK;IACL,KAAK;IACL,OAAO;IACP,QAAQ;GACT;GACA,QAAQ,CAAC,QAAQ,OAAO;GACxB,UAAU;EACX,CA6DC;EACA,OA3Da,gBAAgB;GAC7B,MAAM;GACN,OAAO;IACN,OAAO;IACP,aAAa;IACb,OAAO;IACP,MAAM;KACL;KACA;KACA;KACA;KACA;IACD;IACA,WAAW;IACX,UAAU;GACX;GACA,QAAQ,CAAC,UAAU,QAAQ;GAC3B,UAAU;EACX,CAyCC;EACA,MAvCY,gBAAgB;GAC5B,MAAM;GACN,OAAO,EAAE,SAAS;IACjB;IACA;IACA;GACD,EAAE;GACF,UAAU;GACV,QAAQ;EACT,CA8BC;EACA,MA5BY,gBAAgB;GAC5B,MAAM;GACN,OAAO;IACN,WAAW,CAAC,UAAU,KAAK;IAC3B,KAAK;KACJ;KACA;KACA;KACA;IACD;IACA,OAAO;KACN;KACA;KACA;KACA;IACD;IACA,SAAS,CAAC,SAAS,MAAM;GAC1B;GACA,QAAQ,CAAC,KAAK;GACd,UAAU;EACX,CAQC;CACD;CACwB,kBAAkB,OAAO,OAAO,iBAAiB,CAAC;CA4I1E,SAAS,uBAAuB,SAAS;EACxC,OAAO;GACN,OAAO,YAAY,QAAQ,YAAY,OAAO;GAC9C,OAAO,UAAU;IAChB,MAAM,aAAa,UAAU,SAAS,MAAM,IAAI;IAChD,QAAQ,iBAAiB,WAAW,SAAS;IAC7C,aAAa,QAAQ,oBAAoB,WAAW,SAAS;GAC9D;EACD;CACD;CCjUA,MAAa,cAAc;;;CCG3B,SAAS,OAAO,MAA6B;EAC3C,OAAO,OAAQ,MAA+B,QAAQ;CACxD;CAEA,SAAgB,cAAc,SAA+B;EAC3D,OAAO;GACL,OAAO,YAAY,QAAQ,KAAK,EAAE,KAAK,KAAK,UAAU,OAAO,EAAE,CAAgB;GAC/E,SAAS,aACP,QAAQ,QAAQ,SAAS;IACvB,IAAI,OAAO,IAAI,GACb,SAAS,KAAK,MAAM,KAAK,GAAG,CAAC;GAEjC,CAAC;EACL;CACF;;;CCZA,MAAM,QAAQ;CAEd,WAAW,kBAAkB,cAAc,uBAAuB,KAAK,CAAC;CAExE,MAAM,cAAc,WAAW"}
|