@metapages/metapage 1.8.29 → 1.9.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/README.md +426 -52
- package/package.json +2 -1
- package/src/metapage/MetapageIFrameRpcClient.ts +14 -13
- package/src/metapage/conversions-metaframe.ts +32 -3
- package/src/metapage/v2/metaframe.ts +29 -2
- package/dist/index.d.ts +0 -20
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -3705
- package/dist/index.js.map +0 -1
- package/dist/metapage/Constants.d.ts +0 -6
- package/dist/metapage/Constants.d.ts.map +0 -1
- package/dist/metapage/Metaframe.d.ts +0 -79
- package/dist/metapage/Metaframe.d.ts.map +0 -1
- package/dist/metapage/Metapage.d.ts +0 -105
- package/dist/metapage/Metapage.d.ts.map +0 -1
- package/dist/metapage/MetapageIFrameRpcClient.d.ts +0 -64
- package/dist/metapage/MetapageIFrameRpcClient.d.ts.map +0 -1
- package/dist/metapage/MetapageTools.d.ts +0 -21
- package/dist/metapage/MetapageTools.d.ts.map +0 -1
- package/dist/metapage/Shared.d.ts +0 -16
- package/dist/metapage/Shared.d.ts.map +0 -1
- package/dist/metapage/conversions-metaframe.d.ts +0 -14
- package/dist/metapage/conversions-metaframe.d.ts.map +0 -1
- package/dist/metapage/conversions-metapage.d.ts +0 -9
- package/dist/metapage/conversions-metapage.d.ts.map +0 -1
- package/dist/metapage/core.d.ts +0 -7
- package/dist/metapage/core.d.ts.map +0 -1
- package/dist/metapage/data.d.ts +0 -25
- package/dist/metapage/data.d.ts.map +0 -1
- package/dist/metapage/errors/MissingMetaframeJson.d.ts +0 -10
- package/dist/metapage/errors/MissingMetaframeJson.d.ts.map +0 -1
- package/dist/metapage/errors/RootMetapageError.d.ts +0 -10
- package/dist/metapage/errors/RootMetapageError.d.ts.map +0 -1
- package/dist/metapage/events.d.ts +0 -24
- package/dist/metapage/events.d.ts.map +0 -1
- package/dist/metapage/jsonrpc.d.ts +0 -35
- package/dist/metapage/jsonrpc.d.ts.map +0 -1
- package/dist/metapage/jsonrpc2.d.ts +0 -35
- package/dist/metapage/jsonrpc2.d.ts.map +0 -1
- package/dist/metapage/metapageRenderer.d.ts +0 -42
- package/dist/metapage/metapageRenderer.d.ts.map +0 -1
- package/dist/metapage/metapageRendererExample.d.ts +0 -13
- package/dist/metapage/metapageRendererExample.d.ts.map +0 -1
- package/dist/metapage/util.d.ts +0 -8
- package/dist/metapage/util.d.ts.map +0 -1
- package/dist/metapage/v0_1_0/all.d.ts +0 -110
- package/dist/metapage/v0_1_0/all.d.ts.map +0 -1
- package/dist/metapage/v0_2/all.d.ts +0 -68
- package/dist/metapage/v0_2/all.d.ts.map +0 -1
- package/dist/metapage/v0_3/JsonRpcMethods.d.ts +0 -42
- package/dist/metapage/v0_3/JsonRpcMethods.d.ts.map +0 -1
- package/dist/metapage/v0_3/all.d.ts +0 -11
- package/dist/metapage/v0_3/all.d.ts.map +0 -1
- package/dist/metapage/v0_4/index.d.ts +0 -3
- package/dist/metapage/v0_4/index.d.ts.map +0 -1
- package/dist/metapage/v0_4/metaframe.d.ts +0 -136
- package/dist/metapage/v0_4/metaframe.d.ts.map +0 -1
- package/dist/metapage/v0_4/metapage.d.ts +0 -34
- package/dist/metapage/v0_4/metapage.d.ts.map +0 -1
- package/dist/metapage/v1/index.d.ts +0 -3
- package/dist/metapage/v1/index.d.ts.map +0 -1
- package/dist/metapage/v1/metaframe.d.ts +0 -59
- package/dist/metapage/v1/metaframe.d.ts.map +0 -1
- package/dist/metapage/v1/metapage.d.ts +0 -26
- package/dist/metapage/v1/metapage.d.ts.map +0 -1
- package/dist/metapage/v2/index.d.ts +0 -3
- package/dist/metapage/v2/index.d.ts.map +0 -1
- package/dist/metapage/v2/metaframe.d.ts +0 -25
- package/dist/metapage/v2/metaframe.d.ts.map +0 -1
- package/dist/metapage/v2/metapage.d.ts +0 -22
- package/dist/metapage/v2/metapage.d.ts.map +0 -1
- package/dist/metapage/versions.d.ts +0 -7
- package/dist/metapage/versions.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,38 +1,173 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @metapages/metapage
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Build composable, interconnected web applications using iframes and data pipes
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- Documentation: [https://docs.metapage.io/docs](https://docs.metapage.io/docs)
|
|
7
|
-
- Online tests and conversion tools: [https://module.metapage.io](https://module.metapage.io)
|
|
8
|
-
- Example javascript component: [https://js.mtfm.io](https://js.mtfm.io)
|
|
5
|
+
[](https://www.npmjs.com/package/@metapages/metapage)
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
**@metapages/metapage** is a JavaScript library that lets you create and embed interactive workflows in the browser by connecting independent iframe components together through input/output data pipes.
|
|
11
8
|
|
|
12
|
-
##
|
|
9
|
+
## Quick Links
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
- 📚 [Full Documentation](https://docs.metapage.io/docs)
|
|
12
|
+
- 🎨 [Live Examples](https://metapage.io)
|
|
13
|
+
- 🛠️ [Interactive Tools](https://module.metapage.io)
|
|
14
|
+
- 💻 [Example Component](https://js.mtfm.io)
|
|
15
|
+
|
|
16
|
+
## What is this?
|
|
17
|
+
|
|
18
|
+
A **metapage** is a web application made up of connected iframes called **metaframes**. Each metaframe can:
|
|
19
|
+
|
|
20
|
+
- Receive data from other metaframes (inputs)
|
|
21
|
+
- Send data to other metaframes (outputs)
|
|
22
|
+
- Run independently (JavaScript, Docker containers, markdown editors, or any web component)
|
|
23
|
+
|
|
24
|
+
Think of it like a visual programming environment where each component is a full web application that can communicate with others through simple JSON data pipes.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @metapages/metapage
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or use directly from CDN:
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
import {
|
|
36
|
+
renderMetapage,
|
|
37
|
+
Metapage,
|
|
38
|
+
Metaframe,
|
|
39
|
+
} from "https://cdn.jsdelivr.net/npm/@metapages/metapage@1.8.35";
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
### Rendering a Metapage
|
|
45
|
+
|
|
46
|
+
The simplest way to embed a workflow is using `renderMetapage`:
|
|
15
47
|
|
|
16
48
|
```javascript
|
|
17
|
-
import { renderMetapage } from "
|
|
49
|
+
import { renderMetapage } from "@metapages/metapage";
|
|
18
50
|
|
|
19
|
-
//
|
|
20
|
-
// or just use your own definition JSON
|
|
51
|
+
// Fetch a metapage definition (or define your own JSON)
|
|
21
52
|
const response = await fetch(
|
|
22
53
|
"https://metapage.io/m/87ae11673508447e883b598bf7da9c5d/metapage.json",
|
|
23
54
|
);
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
55
|
+
const definition = await response.json();
|
|
56
|
+
|
|
57
|
+
// Render it
|
|
58
|
+
const { setInputs, dispose } = await renderMetapage({
|
|
59
|
+
definition,
|
|
60
|
+
rootDiv: document.getElementById("container"),
|
|
28
61
|
});
|
|
29
62
|
```
|
|
30
63
|
|
|
31
|
-
The
|
|
64
|
+
The `renderMetapage` function the `react-grid-layout` layout in `metapage.json`:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"meta": {
|
|
69
|
+
"layouts": {
|
|
70
|
+
"react-grid-layout": {
|
|
71
|
+
...
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
[Implentation in source code](https://github.com/metapages/metapage/blob/2ef8bd7bfb151ad1616da46aa9797bcf2b1c3d78/app/libs/src/metapage/metapageRenderer.ts#L204)
|
|
79
|
+
|
|
80
|
+
### Creating a Metaframe (Inside an iframe)
|
|
32
81
|
|
|
33
|
-
|
|
82
|
+
If you're building a component to use in a metapage:
|
|
34
83
|
|
|
35
|
-
|
|
84
|
+
```javascript
|
|
85
|
+
import { Metaframe } from "https://cdn.jsdelivr.net/npm/@metapages/metapage@1.8.35";
|
|
86
|
+
|
|
87
|
+
const metaframe = new Metaframe();
|
|
88
|
+
|
|
89
|
+
// Listen for input data from other metaframes
|
|
90
|
+
metaframe.onInput("data", (value) => {
|
|
91
|
+
console.log("Received:", value);
|
|
92
|
+
// Process the data and send output
|
|
93
|
+
metaframe.setOutput("result", value.toUpperCase());
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Or listen to all inputs at once
|
|
97
|
+
metaframe.onInputs((inputs) => {
|
|
98
|
+
console.log("All inputs:", inputs);
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Core Concepts
|
|
103
|
+
|
|
104
|
+
### Metapage Definition
|
|
105
|
+
|
|
106
|
+
A metapage is defined using JSON that specifies which metaframes to load and how they connect:
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
{
|
|
110
|
+
"version": "2",
|
|
111
|
+
"metaframes": {
|
|
112
|
+
"input": {
|
|
113
|
+
"url": "https://editor.mtfm.io/#?hm=disabled"
|
|
114
|
+
},
|
|
115
|
+
"processor": {
|
|
116
|
+
"url": "https://js.mtfm.io/",
|
|
117
|
+
"inputs": [
|
|
118
|
+
{
|
|
119
|
+
"metaframe": "input",
|
|
120
|
+
"source": "text",
|
|
121
|
+
"target": "code"
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
"output": {
|
|
126
|
+
"url": "https://markdown.mtfm.io/",
|
|
127
|
+
"inputs": [
|
|
128
|
+
{
|
|
129
|
+
"metaframe": "processor",
|
|
130
|
+
"source": "output"
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
This creates a pipeline: `input` → `processor` → `output`
|
|
139
|
+
|
|
140
|
+
### Data Pipes
|
|
141
|
+
|
|
142
|
+
Pipes connect metaframe outputs to other metaframe inputs:
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
{
|
|
146
|
+
"metaframe": "sourceMetaframeId", // Where data comes from
|
|
147
|
+
"source": "outputPipeName", // Name of the output pipe
|
|
148
|
+
"target": "inputPipeName" // Name of the input pipe (optional, defaults to source)
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Working with Data
|
|
153
|
+
|
|
154
|
+
The library automatically handles serialization of complex data types:
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
// In a metaframe - these are automatically serialized when sent between iframes
|
|
158
|
+
metaframe.setOutput("file", new File([blob], "data.txt"));
|
|
159
|
+
metaframe.setOutput("binary", new Uint8Array([1, 2, 3]));
|
|
160
|
+
metaframe.setOutput("buffer", arrayBuffer);
|
|
161
|
+
|
|
162
|
+
// And automatically deserialized when received
|
|
163
|
+
metaframe.onInput("file", (file) => {
|
|
164
|
+
console.log(file instanceof File); // true
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Usage Examples
|
|
169
|
+
|
|
170
|
+
### Full HTML Example
|
|
36
171
|
|
|
37
172
|
```html
|
|
38
173
|
<!DOCTYPE html>
|
|
@@ -40,61 +175,300 @@ The rendered workflow:
|
|
|
40
175
|
<head>
|
|
41
176
|
<meta charset="utf-8" />
|
|
42
177
|
<style>
|
|
43
|
-
|
|
44
|
-
box-sizing: border-box;
|
|
178
|
+
body {
|
|
45
179
|
margin: 0;
|
|
46
180
|
padding: 0;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
body {
|
|
50
181
|
width: 100vw;
|
|
51
182
|
height: 100vh;
|
|
52
183
|
}
|
|
53
|
-
|
|
54
|
-
iframe {
|
|
55
|
-
overflow: hidden;
|
|
56
|
-
border: 0;
|
|
57
|
-
height: 100%;
|
|
184
|
+
#metapage-container {
|
|
58
185
|
width: 100%;
|
|
186
|
+
height: 100%;
|
|
59
187
|
}
|
|
60
188
|
</style>
|
|
61
189
|
</head>
|
|
62
|
-
|
|
63
190
|
<body>
|
|
64
|
-
<div
|
|
191
|
+
<div id="metapage-container"></div>
|
|
65
192
|
|
|
66
193
|
<script type="module">
|
|
67
|
-
|
|
68
|
-
import { renderMetapage } from "https://cdn.jsdelivr.net/npm/@metapages/metapage@1.8.15";
|
|
194
|
+
import { renderMetapage } from "https://cdn.jsdelivr.net/npm/@metapages/metapage@1.8.35";
|
|
69
195
|
|
|
70
|
-
|
|
71
|
-
// or just use your own definition JSON
|
|
72
|
-
const response = await fetch(
|
|
196
|
+
const definition = await fetch(
|
|
73
197
|
"https://metapage.io/m/87ae11673508447e883b598bf7da9c5d/metapage.json",
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const onOutputs = (outputs) => {
|
|
79
|
-
// do something with the outputs
|
|
80
|
-
console.log("outputs", outputs);
|
|
81
|
-
};
|
|
82
|
-
// set input values with the provided setInputs function
|
|
83
|
-
// dispose removes all event listeners and unloads the metapage
|
|
84
|
-
const { setInputs, dispose } = await renderMetapage({
|
|
85
|
-
definition: metapageDefinition,
|
|
86
|
-
onOutputs,
|
|
198
|
+
).then((r) => r.json());
|
|
199
|
+
|
|
200
|
+
const { setInputs, dispose, metapage } = await renderMetapage({
|
|
201
|
+
definition,
|
|
87
202
|
rootDiv: document.getElementById("metapage-container"),
|
|
203
|
+
onOutputs: (outputs) => {
|
|
204
|
+
console.log("Metaframe outputs:", outputs);
|
|
205
|
+
},
|
|
88
206
|
options: {
|
|
89
|
-
hideBorder: true,
|
|
90
207
|
hideFrameBorders: true,
|
|
91
208
|
hideOptions: true,
|
|
92
|
-
hideMetaframeLabels: true,
|
|
93
209
|
},
|
|
94
210
|
});
|
|
211
|
+
|
|
212
|
+
// Send inputs to metaframes
|
|
213
|
+
setInputs({
|
|
214
|
+
metaframeId: {
|
|
215
|
+
inputPipeName: "some value",
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Clean up when done
|
|
220
|
+
// dispose();
|
|
221
|
+
</script>
|
|
222
|
+
</body>
|
|
223
|
+
</html>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Building a Metaframe Component
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
import { Metaframe } from "https://cdn.jsdelivr.net/npm/@metapages/metapage@1.8.35";
|
|
230
|
+
|
|
231
|
+
const metaframe = new Metaframe();
|
|
232
|
+
|
|
233
|
+
// Handle inputs
|
|
234
|
+
metaframe.onInputs((inputs) => {
|
|
235
|
+
const { data, config } = inputs;
|
|
236
|
+
|
|
237
|
+
// Process inputs
|
|
238
|
+
const result = processData(data, config);
|
|
239
|
+
|
|
240
|
+
// Send outputs
|
|
241
|
+
metaframe.setOutputs({
|
|
242
|
+
result: result,
|
|
243
|
+
timestamp: Date.now(),
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// Individual input listener
|
|
248
|
+
metaframe.onInput("reset", () => {
|
|
249
|
+
metaframe.setOutputs({});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Get a specific input value
|
|
253
|
+
const currentValue = metaframe.getInput("data");
|
|
254
|
+
|
|
255
|
+
// Get all inputs
|
|
256
|
+
const allInputs = metaframe.getInputs();
|
|
257
|
+
|
|
258
|
+
// Clean up
|
|
259
|
+
metaframe.dispose();
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Programmatic Metapage Control
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
import { Metapage } from "@metapages/metapage";
|
|
266
|
+
|
|
267
|
+
const metapage = new Metapage({
|
|
268
|
+
definition: {
|
|
269
|
+
version: "2",
|
|
270
|
+
metaframes: {
|
|
271
|
+
viewer: {
|
|
272
|
+
url: "https://markdown.mtfm.io/",
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Listen to metaframe outputs
|
|
279
|
+
metapage.on(Metapage.OUTPUTS, (outputs) => {
|
|
280
|
+
console.log("Outputs from all metaframes:", outputs);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Set inputs to metaframes
|
|
284
|
+
await metapage.setInputs({
|
|
285
|
+
viewer: {
|
|
286
|
+
text: "# Hello World",
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Get current outputs
|
|
291
|
+
const outputs = metapage.getState().metaframes.outputs;
|
|
292
|
+
|
|
293
|
+
// Clean up
|
|
294
|
+
metapage.dispose();
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Advanced Features
|
|
298
|
+
|
|
299
|
+
### Hash Parameters in Metaframes
|
|
300
|
+
|
|
301
|
+
Metaframes can read and write to their URL hash parameters:
|
|
302
|
+
|
|
303
|
+
```javascript
|
|
304
|
+
import {
|
|
305
|
+
getHashParamValueJsonFromWindow,
|
|
306
|
+
setHashParamValueJsonInWindow,
|
|
307
|
+
} from "https://cdn.jsdelivr.net/npm/@metapages/metapage@1.8.35";
|
|
308
|
+
|
|
309
|
+
// Read from URL hash
|
|
310
|
+
const config = getHashParamValueJsonFromWindow("config");
|
|
311
|
+
|
|
312
|
+
// Write to URL hash
|
|
313
|
+
setHashParamValueJsonInWindow("config", { theme: "dark" });
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Pattern Matching in Pipes
|
|
317
|
+
|
|
318
|
+
Use glob patterns to match multiple outputs:
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
{
|
|
322
|
+
"inputs": [
|
|
323
|
+
{
|
|
324
|
+
"metaframe": "source",
|
|
325
|
+
"source": "data/*", // Matches data/foo, data/bar, etc.
|
|
326
|
+
"target": "inputs/"
|
|
327
|
+
}
|
|
328
|
+
]
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Binary Data Handling
|
|
333
|
+
|
|
334
|
+
```javascript
|
|
335
|
+
// Send binary data
|
|
336
|
+
const imageData = await fetch("/image.png").then((r) => r.arrayBuffer());
|
|
337
|
+
metaframe.setOutput("image", imageData);
|
|
338
|
+
|
|
339
|
+
// Receive and use
|
|
340
|
+
metaframe.onInput("image", async (data) => {
|
|
341
|
+
const blob = new Blob([data]);
|
|
342
|
+
const url = URL.createObjectURL(blob);
|
|
343
|
+
document.getElementById("img").src = url;
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## API Overview
|
|
348
|
+
|
|
349
|
+
### renderMetapage(options)
|
|
350
|
+
|
|
351
|
+
Render a metapage into a DOM element.
|
|
352
|
+
|
|
353
|
+
**Parameters:**
|
|
354
|
+
|
|
355
|
+
- `definition`: Metapage definition object
|
|
356
|
+
- `rootDiv`: DOM element to render into
|
|
357
|
+
- `onOutputs`: Callback for metaframe outputs (optional)
|
|
358
|
+
- `options`: Rendering options (optional)
|
|
359
|
+
- `hideBorder`: Hide metapage border
|
|
360
|
+
- `hideFrameBorders`: Hide individual metaframe borders
|
|
361
|
+
- `hideOptions`: Hide options panel
|
|
362
|
+
- `hideMetaframeLabels`: Hide metaframe labels
|
|
363
|
+
|
|
364
|
+
**Returns:** `{ setInputs, setOutputs, dispose, metapage }`
|
|
365
|
+
|
|
366
|
+
### Metapage Class
|
|
367
|
+
|
|
368
|
+
**Methods:**
|
|
369
|
+
|
|
370
|
+
- `setInputs(inputs)`: Set inputs for metaframes
|
|
371
|
+
- `getState()`: Get current state (inputs/outputs)
|
|
372
|
+
- `dispose()`: Clean up and remove all listeners
|
|
373
|
+
- `on(event, handler)`: Listen to events
|
|
374
|
+
|
|
375
|
+
**Events:**
|
|
376
|
+
|
|
377
|
+
- `Metapage.OUTPUTS`: When metaframe outputs change
|
|
378
|
+
- `Metapage.INPUTS`: When metapage inputs change
|
|
379
|
+
- `Metapage.DEFINITION`: When definition changes
|
|
380
|
+
|
|
381
|
+
### Metaframe Class
|
|
382
|
+
|
|
383
|
+
**Methods:**
|
|
384
|
+
|
|
385
|
+
- `setOutput(name, value)`: Set a single output
|
|
386
|
+
- `setOutputs(outputs)`: Set multiple outputs
|
|
387
|
+
- `getInput(name)`: Get a single input value
|
|
388
|
+
- `getInputs()`: Get all input values
|
|
389
|
+
- `onInput(name, callback)`: Listen to specific input
|
|
390
|
+
- `onInputs(callback)`: Listen to all inputs
|
|
391
|
+
- `dispose()`: Clean up
|
|
392
|
+
|
|
393
|
+
**Properties:**
|
|
394
|
+
|
|
395
|
+
- `id`: Metaframe ID assigned by parent metapage
|
|
396
|
+
- `isInputOutputBlobSerialization`: Enable/disable automatic binary serialization
|
|
397
|
+
|
|
398
|
+
## Creating Your Own Metaframes
|
|
399
|
+
|
|
400
|
+
Any web application can become a metaframe by:
|
|
401
|
+
|
|
402
|
+
1. Loading the library
|
|
403
|
+
2. Creating a `Metaframe` instance
|
|
404
|
+
3. Listening for inputs
|
|
405
|
+
4. Sending outputs
|
|
406
|
+
|
|
407
|
+
Example minimal metaframe:
|
|
408
|
+
|
|
409
|
+
```html
|
|
410
|
+
<!DOCTYPE html>
|
|
411
|
+
<html>
|
|
412
|
+
<head>
|
|
413
|
+
<title>My Metaframe</title>
|
|
414
|
+
</head>
|
|
415
|
+
<body>
|
|
416
|
+
<script type="module">
|
|
417
|
+
import { Metaframe } from "https://cdn.jsdelivr.net/npm/@metapages/metapage@1.8.35";
|
|
418
|
+
|
|
419
|
+
const metaframe = new Metaframe();
|
|
420
|
+
|
|
421
|
+
metaframe.onInputs((inputs) => {
|
|
422
|
+
// Your logic here
|
|
423
|
+
metaframe.setOutput("result", "processed: " + JSON.stringify(inputs));
|
|
424
|
+
});
|
|
95
425
|
</script>
|
|
96
426
|
</body>
|
|
97
427
|
</html>
|
|
98
428
|
```
|
|
99
429
|
|
|
100
|
-
|
|
430
|
+
## TypeScript Support
|
|
431
|
+
|
|
432
|
+
Full TypeScript definitions are included:
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
import {
|
|
436
|
+
Metapage,
|
|
437
|
+
Metaframe,
|
|
438
|
+
MetapageDefinitionV2,
|
|
439
|
+
MetaframeInputMap,
|
|
440
|
+
MetapageInstanceInputs,
|
|
441
|
+
} from "https://cdn.jsdelivr.net/npm/@metapages/metapage@1.8.35";
|
|
442
|
+
|
|
443
|
+
const definition: MetapageDefinitionV2 = {
|
|
444
|
+
version: "2",
|
|
445
|
+
metaframes: {
|
|
446
|
+
example: {
|
|
447
|
+
url: "https://example.com",
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
const metapage = new Metapage({ definition });
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
## Browser Support
|
|
456
|
+
|
|
457
|
+
- Chrome 78+
|
|
458
|
+
- Modern browsers with ES2020 support
|
|
459
|
+
- ES modules required
|
|
460
|
+
|
|
461
|
+
## License
|
|
462
|
+
|
|
463
|
+
Apache-2.0
|
|
464
|
+
|
|
465
|
+
## Contributing
|
|
466
|
+
|
|
467
|
+
Issues and pull requests welcome at [https://github.com/metapages/metapage](https://github.com/metapages/metapage)
|
|
468
|
+
|
|
469
|
+
## More Resources
|
|
470
|
+
|
|
471
|
+
- [Documentation](https://docs.metapage.io)
|
|
472
|
+
- [Examples Gallery](https://metapage.io)
|
|
473
|
+
- [Create Metaframes](https://js.mtfm.io)
|
|
474
|
+
- [Community](https://github.com/metapages/metapage/discussions)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metapages/metapage",
|
|
3
3
|
"public": true,
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.9.0",
|
|
5
5
|
"description": "Connect web pages together",
|
|
6
6
|
"repository": "https://github.com/metapages/metapage",
|
|
7
7
|
"homepage": "https://metapage.io/",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"@vitest/browser": "^3.2.4",
|
|
40
40
|
"playwright": "^1.54.2",
|
|
41
41
|
"rollup-plugin-typescript-paths": "^1.4.0",
|
|
42
|
+
"tslib": "^2.6.2",
|
|
42
43
|
"typescript": "^5.3.3",
|
|
43
44
|
"vite": "^4.4.11",
|
|
44
45
|
"vitest": "^3.2.4"
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { EventEmitter, ListenerFn } from "eventemitter3";
|
|
2
2
|
|
|
3
|
+
import { getHashParamValueJsonFromUrl } from "@metapages/hash-query";
|
|
4
|
+
|
|
3
5
|
import { VERSION_METAPAGE } from "./Constants";
|
|
6
|
+
import { convertMetaframeJsonToCurrentVersion } from "./conversions-metaframe";
|
|
7
|
+
import { Disposer, MetaframeId, MetaframePipeId, MetapageId } from "./core";
|
|
4
8
|
import { serializeInputs } from "./data";
|
|
9
|
+
import { MetapageEvents } from "./events";
|
|
10
|
+
import {
|
|
11
|
+
ClientMessageRecievedAck,
|
|
12
|
+
JsonRpcMethodsFromParent,
|
|
13
|
+
MinimumClientMessage,
|
|
14
|
+
SetupIframeServerResponseData,
|
|
15
|
+
} from "./jsonrpc";
|
|
5
16
|
import { JsonRpcRequest } from "./jsonrpc2";
|
|
6
17
|
import {
|
|
7
18
|
log as MetapageToolsLog,
|
|
@@ -10,20 +21,10 @@ import {
|
|
|
10
21
|
stringToRgb,
|
|
11
22
|
} from "./MetapageTools";
|
|
12
23
|
import { MetapageHashParams, MetapageShared } from "./Shared";
|
|
13
|
-
import {
|
|
24
|
+
import { getMetaframeDefinitionFromUrl } from "./util";
|
|
14
25
|
import { MetaframeInputMap, MetapageInstanceInputs } from "./v0_4";
|
|
15
|
-
import {
|
|
16
|
-
import { convertMetaframeJsonToCurrentVersion } from "./conversions-metaframe";
|
|
17
|
-
import { MetapageEvents } from "./events";
|
|
18
|
-
import {
|
|
19
|
-
ClientMessageRecievedAck,
|
|
20
|
-
JsonRpcMethodsFromParent,
|
|
21
|
-
MinimumClientMessage,
|
|
22
|
-
SetupIframeServerResponseData,
|
|
23
|
-
} from "./jsonrpc";
|
|
26
|
+
import { MetaframeDefinitionV2 } from "./v2";
|
|
24
27
|
import { VersionsMetaframe, VersionsMetapage } from "./versions";
|
|
25
|
-
import { getHashParamValueJsonFromUrl } from "@metapages/hash-query";
|
|
26
|
-
import { getMetaframeDefinitionFromUrl } from "./util";
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* This class runs in the parent metapage, and connects the communication pipes
|
|
@@ -326,7 +327,7 @@ export class MetapageIFrameRpcClient extends EventEmitter<
|
|
|
326
327
|
|
|
327
328
|
if (this._metapage.listenerCount(MetapageEvents.Outputs) > 0) {
|
|
328
329
|
var outputsUpdate: MetapageInstanceInputs = {};
|
|
329
|
-
outputsUpdate[this.id] =
|
|
330
|
+
outputsUpdate[this.id] = maybeNewOutputs;
|
|
330
331
|
this._metapage.emit(MetapageEvents.Outputs, outputsUpdate);
|
|
331
332
|
}
|
|
332
333
|
}
|
|
@@ -9,10 +9,34 @@ import {
|
|
|
9
9
|
} from "./v0_4/index.js";
|
|
10
10
|
import { MetaframeDefinitionV1 } from "./v1/index.js";
|
|
11
11
|
import { MetaframeVersionCurrent, type VersionsMetaframe } from "./versions.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
HashParamsObject,
|
|
14
|
+
HashParamsRaw,
|
|
15
|
+
MetaframeDefinitionV2,
|
|
16
|
+
} from "./v2/metaframe.js";
|
|
13
17
|
|
|
14
18
|
const fetchRetry = fetchRetryWrapper(fetch);
|
|
15
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Normalizes hashParams from legacy array format to object format.
|
|
22
|
+
* - Array format ["foo", "bar"] becomes { foo: {}, bar: {} }
|
|
23
|
+
* - Object format is passed through unchanged
|
|
24
|
+
* - undefined/null returns undefined
|
|
25
|
+
*/
|
|
26
|
+
export const normalizeHashParams = (
|
|
27
|
+
hashParams: HashParamsRaw | undefined,
|
|
28
|
+
): HashParamsObject | undefined => {
|
|
29
|
+
if (!hashParams) return undefined;
|
|
30
|
+
if (Array.isArray(hashParams)) {
|
|
31
|
+
const result: HashParamsObject = {};
|
|
32
|
+
for (const key of hashParams) {
|
|
33
|
+
result[key] = {};
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
return hashParams;
|
|
38
|
+
};
|
|
39
|
+
|
|
16
40
|
type AnyMetaframeDefinition =
|
|
17
41
|
| MetaframeDefinitionV03
|
|
18
42
|
| MetaframeDefinitionV4
|
|
@@ -70,7 +94,7 @@ export const convertMetaframeDefinitionToVersion = async (
|
|
|
70
94
|
|
|
71
95
|
export const convertMetaframeDefinitionToCurrentVersion = async (
|
|
72
96
|
def: any | AnyMetaframeDefinition,
|
|
73
|
-
): Promise<
|
|
97
|
+
): Promise<MetaframeDefinitionV2> => {
|
|
74
98
|
return convertMetaframeDefinitionToVersion(def, MetaframeVersionCurrent);
|
|
75
99
|
};
|
|
76
100
|
|
|
@@ -213,7 +237,12 @@ export const convertMetaframeJsonToCurrentVersion = async (
|
|
|
213
237
|
if (!m) {
|
|
214
238
|
return;
|
|
215
239
|
}
|
|
216
|
-
|
|
240
|
+
const converted = await convertMetaframeDefinitionToCurrentVersion(m);
|
|
241
|
+
// Normalize hashParams from array format to object format
|
|
242
|
+
if (converted?.hashParams) {
|
|
243
|
+
converted.hashParams = normalizeHashParams(converted.hashParams);
|
|
244
|
+
}
|
|
245
|
+
return converted;
|
|
217
246
|
};
|
|
218
247
|
|
|
219
248
|
const definition_v0_4_to_v0_3 = (def: MetaframeDefinitionV4) => {
|
|
@@ -2,6 +2,31 @@ import { VersionsMetaframe } from "../versions.js";
|
|
|
2
2
|
import { MetaframePipeDefinition } from "../v0_4/index.js";
|
|
3
3
|
import { MetaframeOperationsV1 } from "../v1/metaframe.js";
|
|
4
4
|
|
|
5
|
+
// Hash parameter types
|
|
6
|
+
export type HashParamType =
|
|
7
|
+
| "string"
|
|
8
|
+
| "stringBase64"
|
|
9
|
+
| "boolean"
|
|
10
|
+
| "json"
|
|
11
|
+
| "File|Blob"
|
|
12
|
+
| "number";
|
|
13
|
+
|
|
14
|
+
export interface HashParamDefinition {
|
|
15
|
+
type?: HashParamType;
|
|
16
|
+
description?: string; // For humans or LLMs
|
|
17
|
+
label?: string; // Short label
|
|
18
|
+
value?: any; // Default value
|
|
19
|
+
allowedValues?: any[]; // Allowed values
|
|
20
|
+
defaultValue?: any; // Default value
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type HashParamsObject = {
|
|
24
|
+
[key: string]: HashParamDefinition;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Union type for raw definitions (before normalization)
|
|
28
|
+
export type HashParamsRaw = string[] | HashParamsObject;
|
|
29
|
+
|
|
5
30
|
export type MetaframeMetadataV2 = {
|
|
6
31
|
name?: string;
|
|
7
32
|
description?: string;
|
|
@@ -27,6 +52,8 @@ export interface MetaframeDefinitionV2 {
|
|
|
27
52
|
// Set or override allowed features for the iframe
|
|
28
53
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox
|
|
29
54
|
sandbox?: string;
|
|
30
|
-
//
|
|
31
|
-
|
|
55
|
+
// Hash parameters configuration.
|
|
56
|
+
// Accepts both legacy array format (string[]) and new object format (HashParamsObject).
|
|
57
|
+
// When fetched via helper methods, array format is normalized to object format.
|
|
58
|
+
hashParams?: HashParamsRaw;
|
|
32
59
|
}
|