@persona/layout-editor-remote-module 0.5.0 → 0.5.1
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 +24 -7
- package/dist/bridge-frame.d.mts +4 -1
- package/dist/bridge-frame.mjs +2 -2
- package/dist/bridge-host.mjs +1 -1
- package/dist/{define-Dt8sbrVO.d.mts → define-C-3P4CFz.d.mts} +11 -24
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{protocol-DMsysRFd.mjs → protocol-Dz3ZX59L.mjs} +3 -7
- package/dist/{schema-BeI8GdXS.mjs → schema-Bc3ZwB8y.mjs} +2 -4
- package/dist/vite-plugin.d.mts +2 -2
- package/dist/vite-plugin.mjs +3 -4
- package/package.json +2 -2
- package/skills/getting-started/SKILL.md +46 -43
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @persona/layout-editor-remote-module
|
|
2
2
|
|
|
3
3
|
Shared manifest format and Vite plugin for remotely-defined dashboard layout modules — used by
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Persona dashboard apps to declare a layout module directly in a typical app's `vite.config.ts`,
|
|
5
|
+
without editing the host.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -81,6 +81,23 @@ import { RemoteModuleFrame } from '@persona/layout-editor-remote-module/bridge-h
|
|
|
81
81
|
|
|
82
82
|
The required `sandbox` value is chosen by the host.
|
|
83
83
|
|
|
84
|
+
By default, the bridge accepts host origins matching `*.withpersona.com`,
|
|
85
|
+
`*.withpersona-staging.com`, `localhost`, and `127.0.0.1`. Add development or preview origins with
|
|
86
|
+
`allowedHostOrigins`, optionally composing the exported `personaDashboardHostOrigins` helper:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import {
|
|
90
|
+
LayoutModuleBridge,
|
|
91
|
+
personaDashboardHostOrigins,
|
|
92
|
+
} from '@persona/layout-editor-remote-module/bridge-frame';
|
|
93
|
+
|
|
94
|
+
<LayoutModuleBridge
|
|
95
|
+
allowedHostOrigins={(origin) => personaDashboardHostOrigins(origin) || myDevHost(origin)}
|
|
96
|
+
>
|
|
97
|
+
{({ data }) => <RiskSummary data={data} />}
|
|
98
|
+
</LayoutModuleBridge>;
|
|
99
|
+
```
|
|
100
|
+
|
|
84
101
|
The document entry uses `LayoutModuleBridge` as a render-prop component:
|
|
85
102
|
|
|
86
103
|
```tsx
|
|
@@ -108,11 +125,11 @@ cannot derive itself:
|
|
|
108
125
|
iframe it arrives through `ready()`, `getContext()`, and the `context` render prop (all
|
|
109
126
|
`RemoteModuleContext | null` until init).
|
|
110
127
|
|
|
111
|
-
The host still appends `access_token`, `environment_id`, and `locale` as query parameters. The
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
128
|
+
The host still appends `access_token`, `environment_id`, and `locale` as query parameters. The app
|
|
129
|
+
hosting the module consumes and strips those parameters itself, so by the time `LayoutModuleBridge`
|
|
130
|
+
runs they have already been handled; the bridge neither reads nor strips them. Keep the host
|
|
131
|
+
iframe's `referrerpolicy` at its default `no-referrer` (or use `strict-origin`) so the URL
|
|
132
|
+
parameters cannot leak through subsequent requests.
|
|
116
133
|
|
|
117
134
|
The bridge's security model is intentionally narrow:
|
|
118
135
|
|
package/dist/bridge-frame.d.mts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { n as Json, t as RemoteModuleContext } from "./protocol-edoRdXpF.mjs";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
|
|
4
|
+
//#region src/bridge/origin.d.ts
|
|
5
|
+
declare function personaDashboardHostOrigins(origin: string): boolean;
|
|
6
|
+
//#endregion
|
|
4
7
|
//#region src/bridge/iframe/createModuleBridge.d.ts
|
|
5
8
|
type ModuleBridgeInitPayload = {
|
|
6
9
|
data: Json;
|
|
@@ -42,4 +45,4 @@ declare function LayoutModuleBridge({
|
|
|
42
45
|
children
|
|
43
46
|
}: LayoutModuleBridgeProps): ReactNode;
|
|
44
47
|
//#endregion
|
|
45
|
-
export { type Json, LayoutModuleBridge, type LayoutModuleBridgeProps, type LayoutModuleBridgeRenderProps, type RemoteModuleContext };
|
|
48
|
+
export { type Json, LayoutModuleBridge, type LayoutModuleBridgeProps, type LayoutModuleBridgeRenderProps, type RemoteModuleContext, personaDashboardHostOrigins };
|
package/dist/bridge-frame.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as resolveAllowedHostOrigins, n as SOURCE, o as candidateParentOrigin, r as parseHostToModuleMessage, s as personaDashboardHostOrigins, t as PROTOCOL_VERSION } from "./protocol-
|
|
1
|
+
import { c as resolveAllowedHostOrigins, n as SOURCE, o as candidateParentOrigin, r as parseHostToModuleMessage, s as personaDashboardHostOrigins, t as PROTOCOL_VERSION } from "./protocol-Dz3ZX59L.mjs";
|
|
2
2
|
import { Component, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
|
|
@@ -347,4 +347,4 @@ function LayoutModuleBridge({ allowedHostOrigins, resolveTimeoutMs, observeHeigh
|
|
|
347
347
|
}
|
|
348
348
|
|
|
349
349
|
//#endregion
|
|
350
|
-
export { LayoutModuleBridge };
|
|
350
|
+
export { LayoutModuleBridge, personaDashboardHostOrigins };
|
package/dist/bridge-host.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as assertNotSelfOrigin, i as parseModuleToHostMessage, n as SOURCE, t as PROTOCOL_VERSION } from "./protocol-
|
|
1
|
+
import { a as assertNotSelfOrigin, i as parseModuleToHostMessage, n as SOURCE, t as PROTOCOL_VERSION } from "./protocol-Dz3ZX59L.mjs";
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
|
|
@@ -79,31 +79,20 @@ type RemoteModuleRegistrationManifestV1Schema = typeof remoteModuleRegistrationM
|
|
|
79
79
|
type Json = boolean | JsonObject | JsonValue[] | null | number | string;
|
|
80
80
|
/**
|
|
81
81
|
* Loose JSON Schema object. Deliberately not a full JSON Schema type system —
|
|
82
|
-
* this package only needs to carry what `z.toJSONSchema()` emits
|
|
83
|
-
* to
|
|
84
|
-
* it on the other.
|
|
82
|
+
* this package only needs to carry what `z.toJSONSchema()` emits for consumers
|
|
83
|
+
* to validate or render.
|
|
85
84
|
*/
|
|
86
85
|
type JsonSchemaObject = Record<string, unknown>;
|
|
87
|
-
/**
|
|
88
|
-
* Mirrors persona-web's `LayoutType`
|
|
89
|
-
* (front-end/dashboard/modules/lib/types.ts).
|
|
90
|
-
*/
|
|
86
|
+
/** Mirrors the host's supported layout values. */
|
|
91
87
|
type LayoutType = z.infer<typeof layoutTypeSchema>;
|
|
92
|
-
/**
|
|
93
|
-
* Mirrors persona-web's `ModuleBadge`
|
|
94
|
-
* (front-end/dashboard/modules/lib/config-constants.tsx).
|
|
95
|
-
*/
|
|
88
|
+
/** Mirrors the host's supported module badges. */
|
|
96
89
|
type ModuleBadge = z.infer<typeof moduleBadgeSchema>;
|
|
97
|
-
/**
|
|
98
|
-
* Mirrors persona-web's `ModuleCategory` values
|
|
99
|
-
* (front-end/dashboard/modules/lib/module-library-config.ts).
|
|
100
|
-
*/
|
|
90
|
+
/** Mirrors the host's module-library categories. */
|
|
101
91
|
type ModuleCategory = z.infer<typeof moduleCategorySchema>;
|
|
102
92
|
/**
|
|
103
|
-
* Options passed to a remote module's `defaultConfig` export.
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* scaffolding a remote module has no business branching on.
|
|
93
|
+
* Options passed to a remote module's `defaultConfig` export. Narrower than the
|
|
94
|
+
* host's default-config options; host-internal flags are not exposed to remote
|
|
95
|
+
* modules.
|
|
107
96
|
*/
|
|
108
97
|
interface RemoteModuleDefaultConfigOptions {
|
|
109
98
|
layoutType: LayoutType;
|
|
@@ -119,7 +108,7 @@ type RemoteModuleDelivery = z.infer<typeof remoteDeliverySchema>;
|
|
|
119
108
|
* optional and feature-detected by name — see the "one URL, conventional
|
|
120
109
|
* named exports" design decision. Rendering/sandboxing `render` is out of
|
|
121
110
|
* scope for this package; only `defaultConfig` has a concrete consumer here
|
|
122
|
-
* (
|
|
111
|
+
* (the host's default-config loader). Not applicable to a `document`
|
|
123
112
|
* delivery — nothing is ever imported for those, so none of these exports are
|
|
124
113
|
* reachable.
|
|
125
114
|
*/
|
|
@@ -133,10 +122,8 @@ interface RemoteModuleExports {
|
|
|
133
122
|
* Wire format for a remotely-defined layout module. Must stay
|
|
134
123
|
* JSON-serializable: no functions, no ReactNode, no zod.
|
|
135
124
|
*
|
|
136
|
-
* Producers: `defineLayoutModule()`
|
|
137
|
-
*
|
|
138
|
-
* Consumers: persona-web's `manifestToRegistration()`
|
|
139
|
-
* (front-end/dashboard/modules/lib/remote/adapt.ts).
|
|
125
|
+
* Producers: `defineLayoutModule()` and this package's Vite plugin.
|
|
126
|
+
* Consumers: the host application.
|
|
140
127
|
*/
|
|
141
128
|
type RemoteModuleRegistrationManifestV1 = z.infer<typeof remoteModuleRegistrationManifestV1Schema>;
|
|
142
129
|
type JsonObject = {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as JsonSchemaObject, c as ModuleCategory, d as RemoteModuleExports, f as RemoteModuleRegistrationManifestV1, i as Json, l as RemoteModuleDefaultConfigOptions, m as remoteModuleRegistrationManifestV1Schema, n as LayoutModuleEntry, o as LayoutType, p as RemoteModuleRegistrationManifestV1Schema, r as defineLayoutModule, s as ModuleBadge, t as LayoutModuleDefinitionInput, u as RemoteModuleDelivery } from "./define-
|
|
1
|
+
import { a as JsonSchemaObject, c as ModuleCategory, d as RemoteModuleExports, f as RemoteModuleRegistrationManifestV1, i as Json, l as RemoteModuleDefaultConfigOptions, m as remoteModuleRegistrationManifestV1Schema, n as LayoutModuleEntry, o as LayoutType, p as RemoteModuleRegistrationManifestV1Schema, r as defineLayoutModule, s as ModuleBadge, t as LayoutModuleDefinitionInput, u as RemoteModuleDelivery } from "./define-C-3P4CFz.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
//#region src/manifest-path.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as DEFAULT_MANIFEST_PATH, r as defineLayoutModule, t as remoteModuleRegistrationManifestV1Schema } from "./schema-
|
|
1
|
+
import { n as DEFAULT_MANIFEST_PATH, r as defineLayoutModule, t as remoteModuleRegistrationManifestV1Schema } from "./schema-Bc3ZwB8y.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
//#region src/parse.ts
|
|
@@ -2,11 +2,9 @@ import { z } from "zod";
|
|
|
2
2
|
|
|
3
3
|
//#region src/bridge/origin.ts
|
|
4
4
|
const dashboardHostOriginPattern = /^https:\/\/([a-z0-9-]+\.)*withpersona(-staging)?\.com$/;
|
|
5
|
-
const pagesHostOriginPattern = /^https:\/\/[a-z0-9-]+\.persona-web\.pages\.dev$/;
|
|
6
|
-
const coderHostOriginPattern = /^https:\/\/[a-z0-9-]+\.coder\.development\.personerds\.com$/;
|
|
7
5
|
const localHostOriginPattern = /^http:\/\/(?:localhost|127\.0\.0\.1):\d+$/;
|
|
8
6
|
function personaDashboardHostOrigins(origin) {
|
|
9
|
-
return dashboardHostOriginPattern.test(origin) ||
|
|
7
|
+
return dashboardHostOriginPattern.test(origin) || localHostOriginPattern.test(origin);
|
|
10
8
|
}
|
|
11
9
|
function resolveAllowedHostOrigins(allowedHostOrigins) {
|
|
12
10
|
if (allowedHostOrigins === void 0) return personaDashboardHostOrigins;
|
|
@@ -32,10 +30,8 @@ function assertNotSelfOrigin(moduleOrigin) {
|
|
|
32
30
|
//#endregion
|
|
33
31
|
//#region src/schema.ts
|
|
34
32
|
/**
|
|
35
|
-
* Mirrors
|
|
36
|
-
*
|
|
37
|
-
* mirror persona-web's corresponding enum values and are kept in sync by hand;
|
|
38
|
-
* this package has no dependency on persona-web.
|
|
33
|
+
* Mirrors the host's layout grid column count; the enum schemas below must
|
|
34
|
+
* match the host's values.
|
|
39
35
|
*/
|
|
40
36
|
const MAX_GRID_COLUMNS = 12;
|
|
41
37
|
const layoutTypeSchema = z.enum([
|
|
@@ -28,10 +28,8 @@ const DEFAULT_MANIFEST_PATH = "/_layout_modules_manifest.json";
|
|
|
28
28
|
//#endregion
|
|
29
29
|
//#region src/schema.ts
|
|
30
30
|
/**
|
|
31
|
-
* Mirrors
|
|
32
|
-
*
|
|
33
|
-
* mirror persona-web's corresponding enum values and are kept in sync by hand;
|
|
34
|
-
* this package has no dependency on persona-web.
|
|
31
|
+
* Mirrors the host's layout grid column count; the enum schemas below must
|
|
32
|
+
* match the host's values.
|
|
35
33
|
*/
|
|
36
34
|
const MAX_GRID_COLUMNS = 12;
|
|
37
35
|
const layoutTypeSchema = z.enum([
|
package/dist/vite-plugin.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as LayoutModuleEntry, r as defineLayoutModule, t as LayoutModuleDefinitionInput } from "./define-
|
|
1
|
+
import { n as LayoutModuleEntry, r as defineLayoutModule, t as LayoutModuleDefinitionInput } from "./define-C-3P4CFz.mjs";
|
|
2
2
|
import { Plugin } from "vite";
|
|
3
3
|
|
|
4
4
|
//#region src/vite-plugin.d.ts
|
|
@@ -33,7 +33,7 @@ interface PersonaLayoutModulesOptions {
|
|
|
33
33
|
* wires every `code`-delivered entry into the build as an addressable ESM
|
|
34
34
|
* chunk, ensures every `document`-delivered (`.html`) entry is actually
|
|
35
35
|
* built/served, and emits a `RemoteModuleRegistrationManifestV1[]` document
|
|
36
|
-
*
|
|
36
|
+
* the host can consume.
|
|
37
37
|
*
|
|
38
38
|
* Modules are configured directly here, not discovered by globbing the
|
|
39
39
|
* source tree — a module IS whatever's in `options.modules`, full stop.
|
package/dist/vite-plugin.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as DEFAULT_MANIFEST_PATH, r as defineLayoutModule, t as remoteModuleRegistrationManifestV1Schema } from "./schema-
|
|
1
|
+
import { n as DEFAULT_MANIFEST_PATH, r as defineLayoutModule, t as remoteModuleRegistrationManifestV1Schema } from "./schema-Bc3ZwB8y.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { build } from "esbuild";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
@@ -123,7 +123,7 @@ function ensureRemoteShimFiles() {
|
|
|
123
123
|
* wires every `code`-delivered entry into the build as an addressable ESM
|
|
124
124
|
* chunk, ensures every `document`-delivered (`.html`) entry is actually
|
|
125
125
|
* built/served, and emits a `RemoteModuleRegistrationManifestV1[]` document
|
|
126
|
-
*
|
|
126
|
+
* the host can consume.
|
|
127
127
|
*
|
|
128
128
|
* Modules are configured directly here, not discovered by globbing the
|
|
129
129
|
* source tree — a module IS whatever's in `options.modules`, full stop.
|
|
@@ -366,8 +366,7 @@ function matchDevBundleRequest(url, definitions) {
|
|
|
366
366
|
* array) into an array of `{ find, replacement }`. esbuild's own `alias`
|
|
367
367
|
* option only accepts exact string keys (no RegExp, no partial-path
|
|
368
368
|
* matching) — a real limitation this doesn't try to hide, just the common
|
|
369
|
-
* case: a plain string-to-string alias map, which
|
|
370
|
-
* subapp's `vite.config.ts` actually uses.
|
|
369
|
+
* case: a plain string-to-string alias map, which many apps use.
|
|
371
370
|
*/
|
|
372
371
|
function resolveAliasForEsbuild(resolvedConfig) {
|
|
373
372
|
const alias = {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@persona/layout-editor-remote-module",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "Shared manifest format, Vite plugin, and host/iframe bridge for remotely-defined dashboard layout modules
|
|
3
|
+
"version": "0.5.1",
|
|
4
|
+
"description": "Shared manifest format, Vite plugin, and host/iframe bridge for remotely-defined dashboard layout modules in Persona dashboard apps.",
|
|
5
5
|
"license": "SEE LICENSE",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"types": "./dist/index.d.mts",
|
|
@@ -1,37 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: getting-started
|
|
3
3
|
description: >
|
|
4
|
-
Wire up @persona/layout-editor-remote-module in a
|
|
5
|
-
personaLayoutModules in vite.config.ts, author modules with defineLayoutModule
|
|
6
|
-
(.html) render delivery, add a zod configSchema and an RJSF uiConfigSchema, and
|
|
7
|
-
from the iframed document with LayoutModuleBridge from the bridge-frame entry
|
|
8
|
-
context such as layoutType / layoutObjectId / isReadOnly, resolveDataPath, and
|
|
9
|
-
when adding a new layout module to an app, integrating this package for the
|
|
10
|
-
module document to the dashboard host, or debugging a manifest that fails to
|
|
11
|
-
(duplicate key, missing defaultSize entry, render path not found) or a module
|
|
12
|
-
data from the host.
|
|
4
|
+
Wire up @persona/layout-editor-remote-module in a Persona dashboard app: register
|
|
5
|
+
personaLayoutModules in a typical app's vite.config.ts, author modules with defineLayoutModule
|
|
6
|
+
using a document (.html) render delivery, add a zod configSchema and an RJSF uiConfigSchema, and
|
|
7
|
+
talk to the host from the iframed document with LayoutModuleBridge from the bridge-frame entry
|
|
8
|
+
(layout data, host context such as layoutType / layoutObjectId / isReadOnly, resolveDataPath, and
|
|
9
|
+
height). Activate when adding a new layout module to an app, integrating this package for the
|
|
10
|
+
first time, wiring a module document to the dashboard host, or debugging a manifest that fails to
|
|
11
|
+
build or validate (duplicate key, missing defaultSize entry, render path not found) or a module
|
|
12
|
+
that never receives data from the host.
|
|
13
13
|
metadata:
|
|
14
14
|
type: core
|
|
15
15
|
library: '@persona/layout-editor-remote-module'
|
|
16
|
-
library_version: '0.4.0'
|
|
17
|
-
sources:
|
|
18
|
-
- 'persona-id/persona-layout-editor:packages/remote-module/src/vite-plugin.ts'
|
|
19
|
-
- 'persona-id/persona-layout-editor:packages/remote-module/src/define.ts'
|
|
20
|
-
- 'persona-id/persona-layout-editor:packages/remote-module/src/schema.ts'
|
|
21
|
-
- 'persona-id/persona-layout-editor:packages/remote-module/src/manifest-path.ts'
|
|
22
|
-
- 'persona-id/persona-layout-editor:packages/remote-module/src/bridge/iframe/index.ts'
|
|
23
|
-
- 'persona-id/persona-layout-editor:packages/remote-module/src/bridge/iframe/LayoutModuleBridge.tsx'
|
|
24
|
-
- 'persona-id/persona-layout-editor:packages/remote-module/src/bridge/iframe/createModuleBridge.ts'
|
|
25
|
-
- 'persona-id/persona-layout-editor:packages/remote-module/src/bridge/protocol.ts'
|
|
26
|
-
- 'persona-id/persona-layout-editor:packages/remote-module/README.md'
|
|
27
16
|
---
|
|
28
17
|
|
|
29
18
|
# Getting started with @persona/layout-editor-remote-module
|
|
30
19
|
|
|
31
|
-
Shared manifest format and Vite plugin for remotely-defined dashboard layout modules — lets
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
`{ modules: RemoteModuleRegistrationManifestV1[] }` JSON document
|
|
20
|
+
Shared manifest format and Vite plugin for remotely-defined dashboard layout modules — lets Persona
|
|
21
|
+
dashboard apps declare a layout module directly in a typical app's `vite.config.ts`, without editing
|
|
22
|
+
the host. The plugin builds each module and emits a
|
|
23
|
+
`{ modules: RemoteModuleRegistrationManifestV1[] }` JSON document the host fetches and validates.
|
|
35
24
|
|
|
36
25
|
This package is publicly available from the npm registry.
|
|
37
26
|
|
|
@@ -92,11 +81,11 @@ not relative to `vite.config.ts`'s own directory when `root` is overridden elsew
|
|
|
92
81
|
|
|
93
82
|
### Document delivery — iframed page
|
|
94
83
|
|
|
95
|
-
A module's `render.entry` must end in `.html`:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
84
|
+
A module's `render.entry` must end in `.html`: the host iframes it rather than importing anything.
|
|
85
|
+
Any other extension (`.ts`/`.tsx`/`.js`) builds as a `code` delivery, which the plugin and manifest
|
|
86
|
+
still accept but the host does not render yet — the module shows up in the library and renders
|
|
87
|
+
nothing. Use `path` for a client-routed SPA whose `index.html` already renders the whole app — it
|
|
88
|
+
names the logical route the SPA's own router should land on, not the file's build output path:
|
|
100
89
|
|
|
101
90
|
```ts
|
|
102
91
|
defineLayoutModule({
|
|
@@ -115,7 +104,7 @@ plugin detects that and doesn't inject it again.
|
|
|
115
104
|
|
|
116
105
|
### `configSchema` — validated module configuration
|
|
117
106
|
|
|
118
|
-
Author config as zod; the plugin converts it to JSON Schema (`z.toJSONSchema()`) for
|
|
107
|
+
Author config as zod; the plugin converts it to JSON Schema (`z.toJSONSchema()`) for the host's
|
|
119
108
|
config panel:
|
|
120
109
|
|
|
121
110
|
```ts
|
|
@@ -134,7 +123,7 @@ as static JSON Schema, not re-executed as zod on the consuming side.
|
|
|
134
123
|
|
|
135
124
|
### `uiConfigSchema` — config panel layout
|
|
136
125
|
|
|
137
|
-
|
|
126
|
+
The host renders the config panel with RJSF. Pass an RJSF `uiSchema` (widget choice, field order,
|
|
138
127
|
labels) as `uiConfigSchema`; it is already plain data, so the plugin copies it into the manifest
|
|
139
128
|
unchanged:
|
|
140
129
|
|
|
@@ -154,9 +143,9 @@ defineLayoutModule({
|
|
|
154
143
|
|
|
155
144
|
### `bridge-frame` — talking to the host from the iframed document
|
|
156
145
|
|
|
157
|
-
The host
|
|
158
|
-
|
|
159
|
-
|
|
146
|
+
The host renders a document-delivered module in an iframe and speaks to it over `postMessage`. Wrap
|
|
147
|
+
the module's React root in `LayoutModuleBridge` from the `bridge-frame` entry; it owns the whole
|
|
148
|
+
handshake and hands the module a render prop:
|
|
160
149
|
|
|
161
150
|
```tsx
|
|
162
151
|
import { LayoutModuleBridge } from '@persona/layout-editor-remote-module/bridge-frame';
|
|
@@ -179,7 +168,7 @@ export function App() {
|
|
|
179
168
|
- `context` (`RemoteModuleContext`) is where the module is rendered — host-owned facts the module
|
|
180
169
|
cannot derive itself. It arrives with the initial data and re-renders whenever it changes (see
|
|
181
170
|
below).
|
|
182
|
-
- `resolveDataPath(dataPath, variableMap?)` returns `Promise<Json>`; the host
|
|
171
|
+
- `resolveDataPath(dataPath, variableMap?)` returns `Promise<Json>`; the host owns the
|
|
183
172
|
implementation. It rejects if the host reports an error or does not answer within
|
|
184
173
|
`resolveTimeoutMs` (default 30s). Outside an iframe it rejects immediately, so a module opened
|
|
185
174
|
directly in a tab still renders.
|
|
@@ -189,9 +178,23 @@ export function App() {
|
|
|
189
178
|
- Mount exactly one `LayoutModuleBridge` per document.
|
|
190
179
|
|
|
191
180
|
Props: `fallback?: ReactNode`, `observeHeight?: boolean` (default `true`),
|
|
192
|
-
`resolveTimeoutMs?: number`, `allowedHostOrigins?` (
|
|
193
|
-
|
|
194
|
-
`
|
|
181
|
+
`resolveTimeoutMs?: number`, `allowedHostOrigins?` (to add host origins outside the built-in
|
|
182
|
+
allow-list). Built-in origins include `*.withpersona.com`, `*.withpersona-staging.com`, `localhost`,
|
|
183
|
+
and `127.0.0.1`. Dev and preview dashboards must be added explicitly; compose the built-in predicate
|
|
184
|
+
with an app-specific check:
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import {
|
|
188
|
+
LayoutModuleBridge,
|
|
189
|
+
personaDashboardHostOrigins,
|
|
190
|
+
} from '@persona/layout-editor-remote-module/bridge-frame';
|
|
191
|
+
|
|
192
|
+
<LayoutModuleBridge
|
|
193
|
+
allowedHostOrigins={(origin) => personaDashboardHostOrigins(origin) || myDevHost(origin)}
|
|
194
|
+
>
|
|
195
|
+
{({ data }) => <RiskSummary data={data} />}
|
|
196
|
+
</LayoutModuleBridge>;
|
|
197
|
+
```
|
|
195
198
|
|
|
196
199
|
#### Host context
|
|
197
200
|
|
|
@@ -223,11 +226,11 @@ function RiskSummary({ context }: { context: RemoteModuleContext | null }) {
|
|
|
223
226
|
|
|
224
227
|
#### Access token and environment id
|
|
225
228
|
|
|
226
|
-
The host still appends `access_token`, `environment_id`, and `locale` to the iframe `src`. The
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
229
|
+
The host still appends `access_token`, `environment_id`, and `locale` to the iframe `src`. The app
|
|
230
|
+
hosting the module consumes and strips those parameters itself, so by the time `LayoutModuleBridge`
|
|
231
|
+
runs they have already been handled; the bridge neither reads nor strips them. Keep the host
|
|
232
|
+
iframe's `referrerpolicy` at its default `no-referrer` (or use `strict-origin`) so the URL
|
|
233
|
+
parameters cannot leak through subsequent requests.
|
|
231
234
|
|
|
232
235
|
## Common Mistakes
|
|
233
236
|
|