@persona/layout-editor-remote-module 0.0.0-bootstrap.0 → 0.5.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.
@@ -0,0 +1,217 @@
1
+ import { a as assertNotSelfOrigin, i as parseModuleToHostMessage, n as SOURCE, t as PROTOCOL_VERSION } from "./protocol-DMsysRFd.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,236 @@
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 from one side
83
+ * to whatever a consumer (e.g. persona-web's own `JsonSchemaObject`) does with
84
+ * it on the other.
85
+ */
86
+ type JsonSchemaObject = Record<string, unknown>;
87
+ /**
88
+ * Mirrors persona-web's `LayoutType`
89
+ * (front-end/dashboard/modules/lib/types.ts).
90
+ */
91
+ type LayoutType = z.infer<typeof layoutTypeSchema>;
92
+ /**
93
+ * Mirrors persona-web's `ModuleBadge`
94
+ * (front-end/dashboard/modules/lib/config-constants.tsx).
95
+ */
96
+ 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
+ */
101
+ type ModuleCategory = z.infer<typeof moduleCategorySchema>;
102
+ /**
103
+ * Options passed to a remote module's `defaultConfig` export. Deliberately
104
+ * narrower than persona-web's own `ModuleDefaultConfigOptions` — the migration
105
+ * flags on that type (e.g. `unifiedDataDisplayEnabled`) are host-internal
106
+ * scaffolding a remote module has no business branching on.
107
+ */
108
+ interface RemoteModuleDefaultConfigOptions {
109
+ layoutType: LayoutType;
110
+ position?: {
111
+ x: number;
112
+ y: number;
113
+ };
114
+ }
115
+ /** See `RemoteModuleRegistrationManifestV1['remote']['render']`. */
116
+ type RemoteModuleDelivery = z.infer<typeof remoteDeliverySchema>;
117
+ /**
118
+ * Conventional named exports a `{ code }` delivery's module may provide. All
119
+ * optional and feature-detected by name — see the "one URL, conventional
120
+ * named exports" design decision. Rendering/sandboxing `render` is out of
121
+ * scope for this package; only `defaultConfig` has a concrete consumer here
122
+ * (persona-web's default-config loader). Not applicable to a `document`
123
+ * delivery — nothing is ever imported for those, so none of these exports are
124
+ * reachable.
125
+ */
126
+ interface RemoteModuleExports {
127
+ defaultConfig?: (opts: RemoteModuleDefaultConfigOptions) => {
128
+ config: Json;
129
+ };
130
+ render?: unknown;
131
+ }
132
+ /**
133
+ * Wire format for a remotely-defined layout module. Must stay
134
+ * JSON-serializable: no functions, no ReactNode, no zod.
135
+ *
136
+ * Producers: `defineLayoutModule()` + the Vite plugin (this package), or
137
+ * app-builder's own build pipeline using the same shape.
138
+ * Consumers: persona-web's `manifestToRegistration()`
139
+ * (front-end/dashboard/modules/lib/remote/adapt.ts).
140
+ */
141
+ type RemoteModuleRegistrationManifestV1 = z.infer<typeof remoteModuleRegistrationManifestV1Schema>;
142
+ type JsonObject = {
143
+ [key: string]: Json;
144
+ };
145
+ type JsonValue = Json;
146
+ //#endregion
147
+ //#region src/define.d.ts
148
+ /**
149
+ * Authoring input to `defineLayoutModule()`. This is NOT the wire manifest —
150
+ * it carries a source/page path (`render`, resolved relative to the Vite root)
151
+ * and a live zod schema (`configSchema`), which
152
+ * the Vite plugin (./vite.ts) resolves into a `RemoteModuleRegistrationManifestV1`
153
+ * at build time: a code entry becomes a hashed chunk URL, an html entry
154
+ * becomes its (possibly `path`-overridden) served URL, and `configSchema`
155
+ * becomes JSON Schema via `z.toJSONSchema()`.
156
+ */
157
+ interface LayoutModuleDefinitionInput {
158
+ /**
159
+ * Whether the layout editor allows adding new instances of this module to
160
+ * a layout. Only gates new placements — existing layouts that already
161
+ * contain the module are unaffected. The host ANDs this with its own
162
+ * gating, never ORs.
163
+ */
164
+ allowAddingToLayout: boolean;
165
+ /** Whether the module exposes the dynamic-height option in its configuration. */
166
+ allowDynamicHeight?: boolean;
167
+ badge?: ModuleBadge;
168
+ category?: ModuleCategory;
169
+ /** Authored as zod; converted to JSON Schema by the plugin. */
170
+ configSchema?: z.ZodType;
171
+ defaultSize: Partial<Record<LayoutType, {
172
+ height: number;
173
+ width: number;
174
+ }>>;
175
+ descriptionShort: string;
176
+ displayName: string;
177
+ /**
178
+ * Image path for the module-library tile, origin-relative to the module's
179
+ * source (e.g. `/assets/icon.svg`). The host shows a generic icon when it's
180
+ * omitted or fails to load; a cross-origin or non-http(s) URL is rejected and
181
+ * uses the default icon instead.
182
+ */
183
+ icon?: string;
184
+ internalUsersOnly?: boolean;
185
+ /** Stable key, unique within this app. Never change after release. */
186
+ key: string;
187
+ /** Layout contexts this module supports. Empty or omitted means every layout. */
188
+ layouts?: LayoutType[];
189
+ /** How this module's main view is delivered. See `LayoutModuleEntry`. */
190
+ render: LayoutModuleEntry;
191
+ /**
192
+ * RJSF `uiSchema` for the generated config panel — widget choice, field
193
+ * order, labels. Already plain data (not zod, unlike `configSchema`), so
194
+ * the plugin passes it through to the manifest unchanged.
195
+ */
196
+ uiConfigSchema?: Record<string, unknown>;
197
+ }
198
+ /**
199
+ * An entry a module's `render` slot points at.
200
+ * Resolved relative to the Vite root — there's no separate per-module
201
+ * definition file anymore; modules are configured directly wherever
202
+ * `personaLayoutModules()` is called (typically vite.config.ts).
203
+ *
204
+ * `entry`'s FILE EXTENSION decides delivery kind:
205
+ * - `.html` — a `document` delivery: the host iframes it instead of
206
+ * importing anything. The plugin ensures Vite actually builds/serves
207
+ * this file, detecting whether it's already one of the app's own build
208
+ * entries (e.g. its main `index.html`) and doing nothing extra if so —
209
+ * only injecting it into the build when it isn't already there.
210
+ * - anything else (`.ts`/`.tsx`/`.js`/`.jsx`) — a `code` delivery: built
211
+ * into a chunk and executed directly in the host's own React tree.
212
+ * Exports are feature-detected by conventional name — see
213
+ * `RemoteModuleExports` in types.ts.
214
+ *
215
+ * `path` is only meaningful alongside an `.html` entry, for a client-routed
216
+ * SPA whose `index.html` already renders the whole app: rather than iframing
217
+ * the SPA's root, this names the LOGICAL route its own router should land on
218
+ * (e.g. `/embed/runs/1`) — the final `document` URL IS that path, not the
219
+ * html file's physical output path, since a static/SPA-fallback server
220
+ * serves the same `index.html` either way. Ignored (and meaningless) for a
221
+ * non-`.html` entry.
222
+ */
223
+ interface LayoutModuleEntry {
224
+ entry: string;
225
+ path?: string;
226
+ }
227
+ /**
228
+ * Identity function at runtime — it exists to give authors a typed,
229
+ * discoverable way to build a `personaLayoutModules({ modules: [...] })`
230
+ * entry, nothing more. All real work (path resolution, build-graph
231
+ * injection, schema conversion, manifest emission) happens in the Vite
232
+ * plugin, not here.
233
+ */
234
+ declare function defineLayoutModule(input: LayoutModuleDefinitionInput): LayoutModuleDefinitionInput;
235
+ //#endregion
236
+ 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 };
@@ -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-Dt8sbrVO.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-BeI8GdXS.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 };