@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.
Files changed (73) hide show
  1. package/README.md +426 -52
  2. package/package.json +2 -1
  3. package/src/metapage/MetapageIFrameRpcClient.ts +14 -13
  4. package/src/metapage/conversions-metaframe.ts +32 -3
  5. package/src/metapage/v2/metaframe.ts +29 -2
  6. package/dist/index.d.ts +0 -20
  7. package/dist/index.d.ts.map +0 -1
  8. package/dist/index.js +0 -3705
  9. package/dist/index.js.map +0 -1
  10. package/dist/metapage/Constants.d.ts +0 -6
  11. package/dist/metapage/Constants.d.ts.map +0 -1
  12. package/dist/metapage/Metaframe.d.ts +0 -79
  13. package/dist/metapage/Metaframe.d.ts.map +0 -1
  14. package/dist/metapage/Metapage.d.ts +0 -105
  15. package/dist/metapage/Metapage.d.ts.map +0 -1
  16. package/dist/metapage/MetapageIFrameRpcClient.d.ts +0 -64
  17. package/dist/metapage/MetapageIFrameRpcClient.d.ts.map +0 -1
  18. package/dist/metapage/MetapageTools.d.ts +0 -21
  19. package/dist/metapage/MetapageTools.d.ts.map +0 -1
  20. package/dist/metapage/Shared.d.ts +0 -16
  21. package/dist/metapage/Shared.d.ts.map +0 -1
  22. package/dist/metapage/conversions-metaframe.d.ts +0 -14
  23. package/dist/metapage/conversions-metaframe.d.ts.map +0 -1
  24. package/dist/metapage/conversions-metapage.d.ts +0 -9
  25. package/dist/metapage/conversions-metapage.d.ts.map +0 -1
  26. package/dist/metapage/core.d.ts +0 -7
  27. package/dist/metapage/core.d.ts.map +0 -1
  28. package/dist/metapage/data.d.ts +0 -25
  29. package/dist/metapage/data.d.ts.map +0 -1
  30. package/dist/metapage/errors/MissingMetaframeJson.d.ts +0 -10
  31. package/dist/metapage/errors/MissingMetaframeJson.d.ts.map +0 -1
  32. package/dist/metapage/errors/RootMetapageError.d.ts +0 -10
  33. package/dist/metapage/errors/RootMetapageError.d.ts.map +0 -1
  34. package/dist/metapage/events.d.ts +0 -24
  35. package/dist/metapage/events.d.ts.map +0 -1
  36. package/dist/metapage/jsonrpc.d.ts +0 -35
  37. package/dist/metapage/jsonrpc.d.ts.map +0 -1
  38. package/dist/metapage/jsonrpc2.d.ts +0 -35
  39. package/dist/metapage/jsonrpc2.d.ts.map +0 -1
  40. package/dist/metapage/metapageRenderer.d.ts +0 -42
  41. package/dist/metapage/metapageRenderer.d.ts.map +0 -1
  42. package/dist/metapage/metapageRendererExample.d.ts +0 -13
  43. package/dist/metapage/metapageRendererExample.d.ts.map +0 -1
  44. package/dist/metapage/util.d.ts +0 -8
  45. package/dist/metapage/util.d.ts.map +0 -1
  46. package/dist/metapage/v0_1_0/all.d.ts +0 -110
  47. package/dist/metapage/v0_1_0/all.d.ts.map +0 -1
  48. package/dist/metapage/v0_2/all.d.ts +0 -68
  49. package/dist/metapage/v0_2/all.d.ts.map +0 -1
  50. package/dist/metapage/v0_3/JsonRpcMethods.d.ts +0 -42
  51. package/dist/metapage/v0_3/JsonRpcMethods.d.ts.map +0 -1
  52. package/dist/metapage/v0_3/all.d.ts +0 -11
  53. package/dist/metapage/v0_3/all.d.ts.map +0 -1
  54. package/dist/metapage/v0_4/index.d.ts +0 -3
  55. package/dist/metapage/v0_4/index.d.ts.map +0 -1
  56. package/dist/metapage/v0_4/metaframe.d.ts +0 -136
  57. package/dist/metapage/v0_4/metaframe.d.ts.map +0 -1
  58. package/dist/metapage/v0_4/metapage.d.ts +0 -34
  59. package/dist/metapage/v0_4/metapage.d.ts.map +0 -1
  60. package/dist/metapage/v1/index.d.ts +0 -3
  61. package/dist/metapage/v1/index.d.ts.map +0 -1
  62. package/dist/metapage/v1/metaframe.d.ts +0 -59
  63. package/dist/metapage/v1/metaframe.d.ts.map +0 -1
  64. package/dist/metapage/v1/metapage.d.ts +0 -26
  65. package/dist/metapage/v1/metapage.d.ts.map +0 -1
  66. package/dist/metapage/v2/index.d.ts +0 -3
  67. package/dist/metapage/v2/index.d.ts.map +0 -1
  68. package/dist/metapage/v2/metaframe.d.ts +0 -25
  69. package/dist/metapage/v2/metaframe.d.ts.map +0 -1
  70. package/dist/metapage/v2/metapage.d.ts +0 -22
  71. package/dist/metapage/v2/metapage.d.ts.map +0 -1
  72. package/dist/metapage/versions.d.ts +0 -7
  73. package/dist/metapage/versions.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,38 +1,173 @@
1
- # [Metapages](https://docs.metapage.io/docs)
1
+ # @metapages/metapage
2
2
 
3
- A _metapage_ is a set of connected iframes (_metaframes_).
3
+ > Build composable, interconnected web applications using iframes and data pipes
4
4
 
5
- - Examples: [https://metapage.io](https://metapage.io)
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
+ [![npm version](https://badge.fury.io/js/%40metapages%2Fmetapage.svg)](https://www.npmjs.com/package/@metapages/metapage)
9
6
 
10
- Each component iframe runs the metapage module and establishes input/output pipes. Now you can create, edit, and embed whole complex applications directly in your own apps, or simply create using the [metapage editor]((https://metapage.io) and publish and share immediately.
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
- ## Quickstart metapage
9
+ ## Quick Links
13
10
 
14
- Load and render a metapage definition
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 "https://cdn.jsdelivr.net/npm/@metapages/metapage@1.8.15";
49
+ import { renderMetapage } from "@metapages/metapage";
18
50
 
19
- // download a metapage definition from metapage.io
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 metapageDefinition = await response.json();
25
- await renderMetapage({
26
- definition: metapageDefinition,
27
- rootDiv: document.getElementById("metapage-container"),
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 rendered workflow:
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
- ![Logo](https://unpkg.com/@metapages/metapage/assets/example-01.png)
82
+ If you're building a component to use in a metapage:
34
83
 
35
- ## Full example
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 style="width: 100%; height: 100%" id="metapage-container"></div>
191
+ <div id="metapage-container"></div>
65
192
 
66
193
  <script type="module">
67
- // the only function you need to import to render a metapage
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
- // download a metapage definition from metapage.io
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
- const metapageDefinition = await response.json();
76
-
77
- // handle metapage outputs
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
- Online tests and conversion tools: [https://module.metapage.io](https://module.metapage.io)
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.8.29",
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 { MetaframeDefinitionV2 } from "./v2";
24
+ import { getMetaframeDefinitionFromUrl } from "./util";
14
25
  import { MetaframeInputMap, MetapageInstanceInputs } from "./v0_4";
15
- import { Disposer, MetaframeId, MetaframePipeId, MetapageId } from "./core";
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] = this.outputs;
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 { MetaframeDefinitionV2 } from "./v2/metaframe.js";
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<MetaframeDefinitionV1> => {
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
- return convertMetaframeDefinitionToCurrentVersion(m);
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
- // whitelist allowed hash parameters.
31
- hashParams?: string[];
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
  }