@persona/layout-editor-remote-module 0.0.0-bootstrap.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 +164 -0
- package/dist/bridge-frame.d.mts +48 -0
- package/dist/bridge-frame.mjs +350 -0
- package/dist/bridge-host.d.mts +61 -0
- package/dist/bridge-host.mjs +217 -0
- package/dist/define-C-3P4CFz.d.mts +223 -0
- package/dist/index.d.mts +98 -0
- package/dist/index.mjs +58 -0
- package/dist/protocol-Dz3ZX59L.mjs +207 -0
- package/dist/protocol-edoRdXpF.d.mts +30 -0
- package/dist/schema-Bc3ZwB8y.mjs +103 -0
- package/dist/vite-plugin.d.mts +43 -0
- package/dist/vite-plugin.mjs +434 -0
- package/package.json +117 -4
- package/skills/getting-started/SKILL.md +374 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { a as assertNotSelfOrigin, i as parseModuleToHostMessage, n as SOURCE, t as PROTOCOL_VERSION } from "./protocol-Dz3ZX59L.mjs";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
|
|
5
|
+
//#region src/bridge/host/buildModuleSrc.ts
|
|
6
|
+
const reservedParams = new Set([
|
|
7
|
+
"access_token",
|
|
8
|
+
"environment_id",
|
|
9
|
+
"locale"
|
|
10
|
+
]);
|
|
11
|
+
const credentialParamPattern = /token|secret|password|authorization|api[_-]?key/i;
|
|
12
|
+
function buildModuleSrc(documentUrl, options = {}) {
|
|
13
|
+
const url = new URL(documentUrl, "http://remote-module.invalid");
|
|
14
|
+
for (const [key, value] of Object.entries(options.extraParams ?? {})) {
|
|
15
|
+
if (reservedParams.has(key) || credentialParamPattern.test(key)) throw new Error(`extraParams contains a reserved or credential-like key: ${key}`);
|
|
16
|
+
url.searchParams.set(key, value);
|
|
17
|
+
}
|
|
18
|
+
if (options.accessToken !== void 0) url.searchParams.set("access_token", options.accessToken);
|
|
19
|
+
if (options.environmentId !== void 0) url.searchParams.set("environment_id", options.environmentId);
|
|
20
|
+
if (options.locale !== void 0) url.searchParams.set("locale", options.locale);
|
|
21
|
+
if (/^[a-z][a-z\d+.-]*:\/\//i.test(documentUrl)) return url.toString();
|
|
22
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/bridge/host/createHostBridge.ts
|
|
27
|
+
function createHostBridge({ iframe, moduleOrigin, getData, getContext, resolveDataPath, onHeight, onRenderError, initRetryMs = 250, initRetryTimeoutMs = 1e4 }) {
|
|
28
|
+
assertNotSelfOrigin(moduleOrigin);
|
|
29
|
+
if (moduleOrigin === "*") throw new Error("Remote module origin must be explicit");
|
|
30
|
+
let destroyed = false;
|
|
31
|
+
let initialized = false;
|
|
32
|
+
let initRetryTimer;
|
|
33
|
+
const clearInitRetry = () => {
|
|
34
|
+
if (initRetryTimer !== void 0) {
|
|
35
|
+
clearInterval(initRetryTimer);
|
|
36
|
+
initRetryTimer = void 0;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const post = (message) => {
|
|
40
|
+
if (!destroyed) iframe.contentWindow?.postMessage(message, moduleOrigin);
|
|
41
|
+
};
|
|
42
|
+
const sendInit = () => {
|
|
43
|
+
initialized = true;
|
|
44
|
+
post({
|
|
45
|
+
source: SOURCE,
|
|
46
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
47
|
+
type: "init",
|
|
48
|
+
data: getData(),
|
|
49
|
+
context: getContext()
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
const startInitRetry = () => {
|
|
53
|
+
clearInitRetry();
|
|
54
|
+
sendInit();
|
|
55
|
+
if (initRetryMs <= 0 || initRetryTimeoutMs <= 0) return;
|
|
56
|
+
const deadline = Date.now() + initRetryTimeoutMs;
|
|
57
|
+
initRetryTimer = setInterval(() => {
|
|
58
|
+
if (Date.now() >= deadline) {
|
|
59
|
+
clearInitRetry();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
sendInit();
|
|
63
|
+
}, initRetryMs);
|
|
64
|
+
};
|
|
65
|
+
const sendData = (data, requestId) => {
|
|
66
|
+
post({
|
|
67
|
+
source: SOURCE,
|
|
68
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
69
|
+
type: "data",
|
|
70
|
+
...requestId === void 0 ? {} : { requestId },
|
|
71
|
+
data
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
const onLoad = () => {
|
|
75
|
+
if (!destroyed) startInitRetry();
|
|
76
|
+
};
|
|
77
|
+
const onMessage = (event) => {
|
|
78
|
+
if (destroyed || event.origin !== moduleOrigin || event.source !== iframe.contentWindow) return;
|
|
79
|
+
const message = parseModuleToHostMessage(event.data);
|
|
80
|
+
if (!message) return;
|
|
81
|
+
clearInitRetry();
|
|
82
|
+
switch (message.type) {
|
|
83
|
+
case "ready":
|
|
84
|
+
sendInit();
|
|
85
|
+
break;
|
|
86
|
+
case "request-data":
|
|
87
|
+
sendData(getData(), message.requestId);
|
|
88
|
+
break;
|
|
89
|
+
case "set-height":
|
|
90
|
+
onHeight?.(message.height);
|
|
91
|
+
break;
|
|
92
|
+
case "render-error":
|
|
93
|
+
onRenderError?.(message.message);
|
|
94
|
+
break;
|
|
95
|
+
case "resolve-data-path":
|
|
96
|
+
Promise.resolve().then(() => resolveDataPath(message.dataPath, message.variableMap)).then((result) => {
|
|
97
|
+
post({
|
|
98
|
+
source: SOURCE,
|
|
99
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
100
|
+
type: "data-path-result",
|
|
101
|
+
requestId: message.requestId,
|
|
102
|
+
result: result ?? null
|
|
103
|
+
});
|
|
104
|
+
}, (error) => {
|
|
105
|
+
post({
|
|
106
|
+
source: SOURCE,
|
|
107
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
108
|
+
type: "data-path-result",
|
|
109
|
+
requestId: message.requestId,
|
|
110
|
+
error: error instanceof Error ? error.message : String(error)
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
window.addEventListener("message", onMessage);
|
|
117
|
+
iframe.addEventListener("load", onLoad);
|
|
118
|
+
return {
|
|
119
|
+
pushData(data) {
|
|
120
|
+
if (!destroyed && initialized) sendData(data);
|
|
121
|
+
},
|
|
122
|
+
pushContext(context) {
|
|
123
|
+
if (!destroyed && initialized) post({
|
|
124
|
+
source: SOURCE,
|
|
125
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
126
|
+
type: "context",
|
|
127
|
+
context
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
destroy() {
|
|
131
|
+
if (destroyed) return;
|
|
132
|
+
destroyed = true;
|
|
133
|
+
clearInitRetry();
|
|
134
|
+
window.removeEventListener("message", onMessage);
|
|
135
|
+
iframe.removeEventListener("load", onLoad);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/bridge/host/RemoteModuleFrame.tsx
|
|
142
|
+
function RemoteModuleFrame({ documentUrl, moduleOrigin, accessToken, environmentId, locale, extraParams, title, sandbox, referrerPolicy = "no-referrer", initRetryMs, initRetryTimeoutMs, dynamicHeight = false, heightOffset = 0, minHeight, className, style, data, context, resolveDataPath, onRenderError }) {
|
|
143
|
+
const iframeRef = useRef(null);
|
|
144
|
+
const dataRef = useRef(data);
|
|
145
|
+
const contextRef = useRef(context);
|
|
146
|
+
const onRenderErrorRef = useRef(onRenderError);
|
|
147
|
+
const resolveDataPathRef = useRef(resolveDataPath);
|
|
148
|
+
const bridgeRef = useRef(void 0);
|
|
149
|
+
const [height, setHeight] = useState();
|
|
150
|
+
dataRef.current = data;
|
|
151
|
+
contextRef.current = context;
|
|
152
|
+
onRenderErrorRef.current = onRenderError;
|
|
153
|
+
resolveDataPathRef.current = resolveDataPath;
|
|
154
|
+
const src = useMemo(() => buildModuleSrc(documentUrl, {
|
|
155
|
+
accessToken,
|
|
156
|
+
environmentId,
|
|
157
|
+
locale,
|
|
158
|
+
extraParams
|
|
159
|
+
}), [
|
|
160
|
+
accessToken,
|
|
161
|
+
documentUrl,
|
|
162
|
+
environmentId,
|
|
163
|
+
extraParams,
|
|
164
|
+
locale
|
|
165
|
+
]);
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
const iframe = iframeRef.current;
|
|
168
|
+
if (!iframe) return;
|
|
169
|
+
const bridge = createHostBridge({
|
|
170
|
+
iframe,
|
|
171
|
+
moduleOrigin,
|
|
172
|
+
getData: () => dataRef.current,
|
|
173
|
+
getContext: () => contextRef.current,
|
|
174
|
+
resolveDataPath: (...args) => resolveDataPathRef.current(...args),
|
|
175
|
+
initRetryMs,
|
|
176
|
+
initRetryTimeoutMs,
|
|
177
|
+
onHeight: (nextHeight) => {
|
|
178
|
+
setHeight(nextHeight);
|
|
179
|
+
},
|
|
180
|
+
onRenderError: (message) => onRenderErrorRef.current?.(message)
|
|
181
|
+
});
|
|
182
|
+
bridgeRef.current = bridge;
|
|
183
|
+
return () => {
|
|
184
|
+
bridge.destroy();
|
|
185
|
+
bridgeRef.current = void 0;
|
|
186
|
+
};
|
|
187
|
+
}, [
|
|
188
|
+
initRetryMs,
|
|
189
|
+
initRetryTimeoutMs,
|
|
190
|
+
moduleOrigin,
|
|
191
|
+
src
|
|
192
|
+
]);
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
bridgeRef.current?.pushData(data);
|
|
195
|
+
}, [data]);
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
bridgeRef.current?.pushContext(context);
|
|
198
|
+
}, [context]);
|
|
199
|
+
const computedHeight = dynamicHeight && height !== void 0 ? `${Math.max(height + heightOffset, minHeight ?? 0)}px` : minHeight === void 0 ? "100%" : `${minHeight}px`;
|
|
200
|
+
return /* @__PURE__ */ jsx("iframe", {
|
|
201
|
+
ref: iframeRef,
|
|
202
|
+
src,
|
|
203
|
+
title,
|
|
204
|
+
sandbox,
|
|
205
|
+
referrerPolicy,
|
|
206
|
+
className,
|
|
207
|
+
style: {
|
|
208
|
+
...style,
|
|
209
|
+
height: computedHeight,
|
|
210
|
+
width: "100%",
|
|
211
|
+
border: "none"
|
|
212
|
+
}
|
|
213
|
+
}, src);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
//#endregion
|
|
217
|
+
export { RemoteModuleFrame };
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/schema.d.ts
|
|
4
|
+
declare const layoutTypeSchema: z.ZodEnum<{
|
|
5
|
+
account: "account";
|
|
6
|
+
case: "case";
|
|
7
|
+
transaction: "transaction";
|
|
8
|
+
}>;
|
|
9
|
+
declare const moduleCategorySchema: z.ZodEnum<{
|
|
10
|
+
build_your_own: "build_your_own";
|
|
11
|
+
components: "components";
|
|
12
|
+
management: "management";
|
|
13
|
+
related_records: "related_records";
|
|
14
|
+
}>;
|
|
15
|
+
declare const moduleBadgeSchema: z.ZodEnum<{
|
|
16
|
+
beta: "beta";
|
|
17
|
+
deprecated: "deprecated";
|
|
18
|
+
improved: "improved";
|
|
19
|
+
new: "new";
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* `code` (a chunk URL, direct-`import()`ed) and `document` (a page URL, host
|
|
23
|
+
* iframes it instead) are mutually exclusive — a delivery is one or the
|
|
24
|
+
* other, never both, never neither.
|
|
25
|
+
*/
|
|
26
|
+
declare const remoteDeliverySchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
27
|
+
code: z.ZodString;
|
|
28
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
29
|
+
document: z.ZodString;
|
|
30
|
+
}, z.core.$strip>]>;
|
|
31
|
+
declare const remoteModuleRegistrationManifestV1Schema: z.ZodObject<{
|
|
32
|
+
allowAddingToLayout: z.ZodBoolean;
|
|
33
|
+
allowDynamicHeight: z.ZodOptional<z.ZodBoolean>;
|
|
34
|
+
badge: z.ZodOptional<z.ZodEnum<{
|
|
35
|
+
beta: "beta";
|
|
36
|
+
deprecated: "deprecated";
|
|
37
|
+
improved: "improved";
|
|
38
|
+
new: "new";
|
|
39
|
+
}>>;
|
|
40
|
+
category: z.ZodOptional<z.ZodEnum<{
|
|
41
|
+
build_your_own: "build_your_own";
|
|
42
|
+
components: "components";
|
|
43
|
+
management: "management";
|
|
44
|
+
related_records: "related_records";
|
|
45
|
+
}>>;
|
|
46
|
+
configSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
47
|
+
defaultSize: z.ZodRecord<z.ZodEnum<{
|
|
48
|
+
account: "account";
|
|
49
|
+
case: "case";
|
|
50
|
+
transaction: "transaction";
|
|
51
|
+
}> & z.core.$partial, z.ZodObject<{
|
|
52
|
+
height: z.ZodNumber;
|
|
53
|
+
width: z.ZodNumber;
|
|
54
|
+
}, z.core.$strip>>;
|
|
55
|
+
descriptionShort: z.ZodString;
|
|
56
|
+
displayName: z.ZodString;
|
|
57
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
58
|
+
internalUsersOnly: z.ZodOptional<z.ZodBoolean>;
|
|
59
|
+
key: z.ZodString;
|
|
60
|
+
layouts: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
61
|
+
account: "account";
|
|
62
|
+
case: "case";
|
|
63
|
+
transaction: "transaction";
|
|
64
|
+
}>>>;
|
|
65
|
+
manifestVersion: z.ZodLiteral<"v1">;
|
|
66
|
+
remote: z.ZodObject<{
|
|
67
|
+
css: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
68
|
+
render: z.ZodUnion<readonly [z.ZodObject<{
|
|
69
|
+
code: z.ZodString;
|
|
70
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
71
|
+
document: z.ZodString;
|
|
72
|
+
}, z.core.$strip>]>;
|
|
73
|
+
}, z.core.$strip>;
|
|
74
|
+
uiConfigSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
75
|
+
}, z.core.$strip>;
|
|
76
|
+
type RemoteModuleRegistrationManifestV1Schema = typeof remoteModuleRegistrationManifestV1Schema;
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/types.d.ts
|
|
79
|
+
type Json = boolean | JsonObject | JsonValue[] | null | number | string;
|
|
80
|
+
/**
|
|
81
|
+
* Loose JSON Schema object. Deliberately not a full JSON Schema type system —
|
|
82
|
+
* this package only needs to carry what `z.toJSONSchema()` emits for consumers
|
|
83
|
+
* to validate or render.
|
|
84
|
+
*/
|
|
85
|
+
type JsonSchemaObject = Record<string, unknown>;
|
|
86
|
+
/** Mirrors the host's supported layout values. */
|
|
87
|
+
type LayoutType = z.infer<typeof layoutTypeSchema>;
|
|
88
|
+
/** Mirrors the host's supported module badges. */
|
|
89
|
+
type ModuleBadge = z.infer<typeof moduleBadgeSchema>;
|
|
90
|
+
/** Mirrors the host's module-library categories. */
|
|
91
|
+
type ModuleCategory = z.infer<typeof moduleCategorySchema>;
|
|
92
|
+
/**
|
|
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.
|
|
96
|
+
*/
|
|
97
|
+
interface RemoteModuleDefaultConfigOptions {
|
|
98
|
+
layoutType: LayoutType;
|
|
99
|
+
position?: {
|
|
100
|
+
x: number;
|
|
101
|
+
y: number;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/** See `RemoteModuleRegistrationManifestV1['remote']['render']`. */
|
|
105
|
+
type RemoteModuleDelivery = z.infer<typeof remoteDeliverySchema>;
|
|
106
|
+
/**
|
|
107
|
+
* Conventional named exports a `{ code }` delivery's module may provide. All
|
|
108
|
+
* optional and feature-detected by name — see the "one URL, conventional
|
|
109
|
+
* named exports" design decision. Rendering/sandboxing `render` is out of
|
|
110
|
+
* scope for this package; only `defaultConfig` has a concrete consumer here
|
|
111
|
+
* (the host's default-config loader). Not applicable to a `document`
|
|
112
|
+
* delivery — nothing is ever imported for those, so none of these exports are
|
|
113
|
+
* reachable.
|
|
114
|
+
*/
|
|
115
|
+
interface RemoteModuleExports {
|
|
116
|
+
defaultConfig?: (opts: RemoteModuleDefaultConfigOptions) => {
|
|
117
|
+
config: Json;
|
|
118
|
+
};
|
|
119
|
+
render?: unknown;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Wire format for a remotely-defined layout module. Must stay
|
|
123
|
+
* JSON-serializable: no functions, no ReactNode, no zod.
|
|
124
|
+
*
|
|
125
|
+
* Producers: `defineLayoutModule()` and this package's Vite plugin.
|
|
126
|
+
* Consumers: the host application.
|
|
127
|
+
*/
|
|
128
|
+
type RemoteModuleRegistrationManifestV1 = z.infer<typeof remoteModuleRegistrationManifestV1Schema>;
|
|
129
|
+
type JsonObject = {
|
|
130
|
+
[key: string]: Json;
|
|
131
|
+
};
|
|
132
|
+
type JsonValue = Json;
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/define.d.ts
|
|
135
|
+
/**
|
|
136
|
+
* Authoring input to `defineLayoutModule()`. This is NOT the wire manifest —
|
|
137
|
+
* it carries a source/page path (`render`, resolved relative to the Vite root)
|
|
138
|
+
* and a live zod schema (`configSchema`), which
|
|
139
|
+
* the Vite plugin (./vite.ts) resolves into a `RemoteModuleRegistrationManifestV1`
|
|
140
|
+
* at build time: a code entry becomes a hashed chunk URL, an html entry
|
|
141
|
+
* becomes its (possibly `path`-overridden) served URL, and `configSchema`
|
|
142
|
+
* becomes JSON Schema via `z.toJSONSchema()`.
|
|
143
|
+
*/
|
|
144
|
+
interface LayoutModuleDefinitionInput {
|
|
145
|
+
/**
|
|
146
|
+
* Whether the layout editor allows adding new instances of this module to
|
|
147
|
+
* a layout. Only gates new placements — existing layouts that already
|
|
148
|
+
* contain the module are unaffected. The host ANDs this with its own
|
|
149
|
+
* gating, never ORs.
|
|
150
|
+
*/
|
|
151
|
+
allowAddingToLayout: boolean;
|
|
152
|
+
/** Whether the module exposes the dynamic-height option in its configuration. */
|
|
153
|
+
allowDynamicHeight?: boolean;
|
|
154
|
+
badge?: ModuleBadge;
|
|
155
|
+
category?: ModuleCategory;
|
|
156
|
+
/** Authored as zod; converted to JSON Schema by the plugin. */
|
|
157
|
+
configSchema?: z.ZodType;
|
|
158
|
+
defaultSize: Partial<Record<LayoutType, {
|
|
159
|
+
height: number;
|
|
160
|
+
width: number;
|
|
161
|
+
}>>;
|
|
162
|
+
descriptionShort: string;
|
|
163
|
+
displayName: string;
|
|
164
|
+
/**
|
|
165
|
+
* Image path for the module-library tile, origin-relative to the module's
|
|
166
|
+
* source (e.g. `/assets/icon.svg`). The host shows a generic icon when it's
|
|
167
|
+
* omitted or fails to load; a cross-origin or non-http(s) URL is rejected and
|
|
168
|
+
* uses the default icon instead.
|
|
169
|
+
*/
|
|
170
|
+
icon?: string;
|
|
171
|
+
internalUsersOnly?: boolean;
|
|
172
|
+
/** Stable key, unique within this app. Never change after release. */
|
|
173
|
+
key: string;
|
|
174
|
+
/** Layout contexts this module supports. Empty or omitted means every layout. */
|
|
175
|
+
layouts?: LayoutType[];
|
|
176
|
+
/** How this module's main view is delivered. See `LayoutModuleEntry`. */
|
|
177
|
+
render: LayoutModuleEntry;
|
|
178
|
+
/**
|
|
179
|
+
* RJSF `uiSchema` for the generated config panel — widget choice, field
|
|
180
|
+
* order, labels. Already plain data (not zod, unlike `configSchema`), so
|
|
181
|
+
* the plugin passes it through to the manifest unchanged.
|
|
182
|
+
*/
|
|
183
|
+
uiConfigSchema?: Record<string, unknown>;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* An entry a module's `render` slot points at.
|
|
187
|
+
* Resolved relative to the Vite root — there's no separate per-module
|
|
188
|
+
* definition file anymore; modules are configured directly wherever
|
|
189
|
+
* `personaLayoutModules()` is called (typically vite.config.ts).
|
|
190
|
+
*
|
|
191
|
+
* `entry`'s FILE EXTENSION decides delivery kind:
|
|
192
|
+
* - `.html` — a `document` delivery: the host iframes it instead of
|
|
193
|
+
* importing anything. The plugin ensures Vite actually builds/serves
|
|
194
|
+
* this file, detecting whether it's already one of the app's own build
|
|
195
|
+
* entries (e.g. its main `index.html`) and doing nothing extra if so —
|
|
196
|
+
* only injecting it into the build when it isn't already there.
|
|
197
|
+
* - anything else (`.ts`/`.tsx`/`.js`/`.jsx`) — a `code` delivery: built
|
|
198
|
+
* into a chunk and executed directly in the host's own React tree.
|
|
199
|
+
* Exports are feature-detected by conventional name — see
|
|
200
|
+
* `RemoteModuleExports` in types.ts.
|
|
201
|
+
*
|
|
202
|
+
* `path` is only meaningful alongside an `.html` entry, for a client-routed
|
|
203
|
+
* SPA whose `index.html` already renders the whole app: rather than iframing
|
|
204
|
+
* the SPA's root, this names the LOGICAL route its own router should land on
|
|
205
|
+
* (e.g. `/embed/runs/1`) — the final `document` URL IS that path, not the
|
|
206
|
+
* html file's physical output path, since a static/SPA-fallback server
|
|
207
|
+
* serves the same `index.html` either way. Ignored (and meaningless) for a
|
|
208
|
+
* non-`.html` entry.
|
|
209
|
+
*/
|
|
210
|
+
interface LayoutModuleEntry {
|
|
211
|
+
entry: string;
|
|
212
|
+
path?: string;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Identity function at runtime — it exists to give authors a typed,
|
|
216
|
+
* discoverable way to build a `personaLayoutModules({ modules: [...] })`
|
|
217
|
+
* entry, nothing more. All real work (path resolution, build-graph
|
|
218
|
+
* injection, schema conversion, manifest emission) happens in the Vite
|
|
219
|
+
* plugin, not here.
|
|
220
|
+
*/
|
|
221
|
+
declare function defineLayoutModule(input: LayoutModuleDefinitionInput): LayoutModuleDefinitionInput;
|
|
222
|
+
//#endregion
|
|
223
|
+
export { JsonSchemaObject as a, ModuleCategory as c, RemoteModuleExports as d, RemoteModuleRegistrationManifestV1 as f, Json as i, RemoteModuleDefaultConfigOptions as l, remoteModuleRegistrationManifestV1Schema as m, LayoutModuleEntry as n, LayoutType as o, RemoteModuleRegistrationManifestV1Schema as p, defineLayoutModule as r, ModuleBadge as s, LayoutModuleDefinitionInput as t, RemoteModuleDelivery as u };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/manifest-path.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Default path (site-root-relative) the manifest document is served from and
|
|
8
|
+
* written to, when `PersonaLayoutModulesOptions.manifestPath` isn't
|
|
9
|
+
* overridden. A standalone module (rather than living in vite-plugin.ts, the
|
|
10
|
+
* one place that actually uses it) so it can also be re-exported from the
|
|
11
|
+
* runtime-safe `.` entry — a consumer wiring up something like CORS for the
|
|
12
|
+
* manifest route in its own server, outside of vite.config.ts, needs this
|
|
13
|
+
* default without pulling in vite/esbuild.
|
|
14
|
+
*/
|
|
15
|
+
declare const DEFAULT_MANIFEST_PATH = "/_layout_modules_manifest.json";
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/parse.d.ts
|
|
18
|
+
/**
|
|
19
|
+
* The document the Vite plugin emits at `manifestPath` (and serves from the
|
|
20
|
+
* dev server): every module the app declares, under a single `modules` key.
|
|
21
|
+
*/
|
|
22
|
+
interface RemoteModuleManifestDocument {
|
|
23
|
+
modules: RemoteModuleRegistrationManifestV1[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Module keys must be unique within one document — a consumer identifies a
|
|
27
|
+
* module as `${sourceId}/${key}`, so a duplicate would collide. The plugin
|
|
28
|
+
* already refuses to build such an app; re-checked here so a hand-written or
|
|
29
|
+
* tampered manifest can't slip one through.
|
|
30
|
+
*/
|
|
31
|
+
declare const remoteModuleManifestDocumentSchema: z.ZodObject<{
|
|
32
|
+
modules: z.ZodArray<z.ZodObject<{
|
|
33
|
+
allowAddingToLayout: z.ZodBoolean;
|
|
34
|
+
allowDynamicHeight: z.ZodOptional<z.ZodBoolean>;
|
|
35
|
+
badge: z.ZodOptional<z.ZodEnum<{
|
|
36
|
+
beta: "beta";
|
|
37
|
+
deprecated: "deprecated";
|
|
38
|
+
improved: "improved";
|
|
39
|
+
new: "new";
|
|
40
|
+
}>>;
|
|
41
|
+
category: z.ZodOptional<z.ZodEnum<{
|
|
42
|
+
build_your_own: "build_your_own";
|
|
43
|
+
components: "components";
|
|
44
|
+
management: "management";
|
|
45
|
+
related_records: "related_records";
|
|
46
|
+
}>>;
|
|
47
|
+
configSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
48
|
+
defaultSize: z.ZodRecord<z.ZodEnum<{
|
|
49
|
+
account: "account";
|
|
50
|
+
case: "case";
|
|
51
|
+
transaction: "transaction";
|
|
52
|
+
}> & z.core.$partial, z.ZodObject<{
|
|
53
|
+
height: z.ZodNumber;
|
|
54
|
+
width: z.ZodNumber;
|
|
55
|
+
}, z.core.$strip>>;
|
|
56
|
+
descriptionShort: z.ZodString;
|
|
57
|
+
displayName: z.ZodString;
|
|
58
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
59
|
+
internalUsersOnly: z.ZodOptional<z.ZodBoolean>;
|
|
60
|
+
key: z.ZodString;
|
|
61
|
+
layouts: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
62
|
+
account: "account";
|
|
63
|
+
case: "case";
|
|
64
|
+
transaction: "transaction";
|
|
65
|
+
}>>>;
|
|
66
|
+
manifestVersion: z.ZodLiteral<"v1">;
|
|
67
|
+
remote: z.ZodObject<{
|
|
68
|
+
css: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
69
|
+
render: z.ZodUnion<readonly [z.ZodObject<{
|
|
70
|
+
code: z.ZodString;
|
|
71
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
72
|
+
document: z.ZodString;
|
|
73
|
+
}, z.core.$strip>]>;
|
|
74
|
+
}, z.core.$strip>;
|
|
75
|
+
uiConfigSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
76
|
+
}, z.core.$strip>>;
|
|
77
|
+
}, z.core.$strip>;
|
|
78
|
+
type RemoteModuleManifestParseResult = {
|
|
79
|
+
data: RemoteModuleManifestDocument;
|
|
80
|
+
success: true;
|
|
81
|
+
} | {
|
|
82
|
+
error: RemoteModuleManifestParseError;
|
|
83
|
+
success: false;
|
|
84
|
+
};
|
|
85
|
+
declare class RemoteModuleManifestParseError extends Error {
|
|
86
|
+
readonly issues: z.core.$ZodIssue[];
|
|
87
|
+
constructor(error: z.ZodError);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Validate the raw JSON of a manifest document (e.g. `await response.json()`)
|
|
91
|
+
* and return it typed. Throws `RemoteModuleManifestParseError` if the document
|
|
92
|
+
* is malformed; use `safeParseRemoteModuleManifest` to get a result instead.
|
|
93
|
+
*/
|
|
94
|
+
declare function parseRemoteModuleManifest(json: unknown): RemoteModuleManifestDocument;
|
|
95
|
+
/** Non-throwing variant of `parseRemoteModuleManifest`. */
|
|
96
|
+
declare function safeParseRemoteModuleManifest(json: unknown): RemoteModuleManifestParseResult;
|
|
97
|
+
//#endregion
|
|
98
|
+
export { DEFAULT_MANIFEST_PATH, type Json, type JsonSchemaObject, type LayoutModuleDefinitionInput, type LayoutModuleEntry, type LayoutType, type ModuleBadge, type ModuleCategory, type RemoteModuleDefaultConfigOptions, type RemoteModuleDelivery, type RemoteModuleExports, type RemoteModuleManifestDocument, RemoteModuleManifestParseError, type RemoteModuleManifestParseResult, type RemoteModuleRegistrationManifestV1, type RemoteModuleRegistrationManifestV1Schema, defineLayoutModule, parseRemoteModuleManifest, remoteModuleManifestDocumentSchema, remoteModuleRegistrationManifestV1Schema, safeParseRemoteModuleManifest };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { n as DEFAULT_MANIFEST_PATH, r as defineLayoutModule, t as remoteModuleRegistrationManifestV1Schema } from "./schema-Bc3ZwB8y.mjs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/parse.ts
|
|
5
|
+
/**
|
|
6
|
+
* Module keys must be unique within one document — a consumer identifies a
|
|
7
|
+
* module as `${sourceId}/${key}`, so a duplicate would collide. The plugin
|
|
8
|
+
* already refuses to build such an app; re-checked here so a hand-written or
|
|
9
|
+
* tampered manifest can't slip one through.
|
|
10
|
+
*/
|
|
11
|
+
const remoteModuleManifestDocumentSchema = z.object({ modules: z.array(remoteModuleRegistrationManifestV1Schema) }).superRefine((document, ctx) => {
|
|
12
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13
|
+
document.modules.forEach((module, index) => {
|
|
14
|
+
if (seen.has(module.key)) ctx.addIssue({
|
|
15
|
+
code: "custom",
|
|
16
|
+
message: `duplicate module key "${module.key}"`,
|
|
17
|
+
path: [
|
|
18
|
+
"modules",
|
|
19
|
+
index,
|
|
20
|
+
"key"
|
|
21
|
+
]
|
|
22
|
+
});
|
|
23
|
+
seen.add(module.key);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
var RemoteModuleManifestParseError = class extends Error {
|
|
27
|
+
issues;
|
|
28
|
+
constructor(error) {
|
|
29
|
+
super(`Invalid remote layout module manifest:\n${z.prettifyError(error)}`);
|
|
30
|
+
this.name = "RemoteModuleManifestParseError";
|
|
31
|
+
this.issues = error.issues;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Validate the raw JSON of a manifest document (e.g. `await response.json()`)
|
|
36
|
+
* and return it typed. Throws `RemoteModuleManifestParseError` if the document
|
|
37
|
+
* is malformed; use `safeParseRemoteModuleManifest` to get a result instead.
|
|
38
|
+
*/
|
|
39
|
+
function parseRemoteModuleManifest(json) {
|
|
40
|
+
const result = remoteModuleManifestDocumentSchema.safeParse(json);
|
|
41
|
+
if (!result.success) throw new RemoteModuleManifestParseError(result.error);
|
|
42
|
+
return result.data;
|
|
43
|
+
}
|
|
44
|
+
/** Non-throwing variant of `parseRemoteModuleManifest`. */
|
|
45
|
+
function safeParseRemoteModuleManifest(json) {
|
|
46
|
+
const result = remoteModuleManifestDocumentSchema.safeParse(json);
|
|
47
|
+
if (!result.success) return {
|
|
48
|
+
error: new RemoteModuleManifestParseError(result.error),
|
|
49
|
+
success: false
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
data: result.data,
|
|
53
|
+
success: true
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
export { DEFAULT_MANIFEST_PATH, RemoteModuleManifestParseError, defineLayoutModule, parseRemoteModuleManifest, remoteModuleManifestDocumentSchema, remoteModuleRegistrationManifestV1Schema, safeParseRemoteModuleManifest };
|