@valbuild/shared 0.102.0 → 0.105.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/declarations/src/internal/ApiRoutes.d.ts +12 -2
- package/dist/declarations/src/internal/index.d.ts +1 -0
- package/dist/declarations/src/internal/valCanvasProtocol.d.ts +174 -0
- package/internal/dist/valbuild-shared-internal.cjs.dev.js +153 -39
- package/internal/dist/valbuild-shared-internal.cjs.prod.js +153 -39
- package/internal/dist/valbuild-shared-internal.esm.js +149 -41
- package/package.json +2 -2
|
@@ -430,6 +430,10 @@ export declare const Api: {
|
|
|
430
430
|
schemaSha: z.ZodString;
|
|
431
431
|
sourcesSha: z.ZodString;
|
|
432
432
|
patches: z.ZodArray<z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>>;
|
|
433
|
+
removed: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
434
|
+
patchId: z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>;
|
|
435
|
+
reason: z.ZodString;
|
|
436
|
+
}, z.core.$strip>>>;
|
|
433
437
|
config: z.ZodObject<{
|
|
434
438
|
project: z.ZodOptional<z.ZodString>;
|
|
435
439
|
root: z.ZodOptional<z.ZodString>;
|
|
@@ -813,7 +817,7 @@ export declare const Api: {
|
|
|
813
817
|
schemaSha: z.ZodString;
|
|
814
818
|
sourcesSha: z.ZodString;
|
|
815
819
|
modules: z.ZodRecord<z.ZodString & z.ZodType<ModuleFilePath, string, z.core.$ZodTypeInternals<ModuleFilePath, string>>, z.ZodObject<{
|
|
816
|
-
|
|
820
|
+
preview: z.ZodOptional<z.ZodAny>;
|
|
817
821
|
source: z.ZodOptional<z.ZodAny>;
|
|
818
822
|
baseSource: z.ZodOptional<z.ZodAny>;
|
|
819
823
|
patches: z.ZodOptional<z.ZodObject<{
|
|
@@ -914,7 +918,13 @@ export declare const Api: {
|
|
|
914
918
|
}, z.core.$strip>;
|
|
915
919
|
}, z.core.$strip>, z.ZodObject<{
|
|
916
920
|
status: z.ZodLiteral<200>;
|
|
917
|
-
json: z.ZodObject<{
|
|
921
|
+
json: z.ZodObject<{
|
|
922
|
+
removed: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
923
|
+
patchId: z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>;
|
|
924
|
+
moduleFilePath: z.ZodString & z.ZodType<ModuleFilePath, string, z.core.$ZodTypeInternals<ModuleFilePath, string>>;
|
|
925
|
+
message: z.ZodString;
|
|
926
|
+
}, z.core.$strip>>>;
|
|
927
|
+
}, z.core.$strip>;
|
|
918
928
|
}, z.core.$strip>, z.ZodObject<{
|
|
919
929
|
status: z.ZodLiteral<409>;
|
|
920
930
|
json: z.ZodObject<{
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The protocol between the studio's canvas and the page inside it.
|
|
3
|
+
*
|
|
4
|
+
* The canvas shows the running site in a frame. That frame is a different
|
|
5
|
+
* document — the customer's app, rendered by their components — so the studio
|
|
6
|
+
* cannot reach into it, and it cannot reach out. Everything they say to each
|
|
7
|
+
* other goes through `postMessage`, and this is the vocabulary.
|
|
8
|
+
*
|
|
9
|
+
* It lives in `shared` because both halves have to agree on it: the page half
|
|
10
|
+
* ships in `@valbuild/next`, the studio half in `@valbuild/ui`, and a message
|
|
11
|
+
* shape defined twice is a message shape that will differ once.
|
|
12
|
+
*
|
|
13
|
+
* ## Why a query parameter turns it on
|
|
14
|
+
*
|
|
15
|
+
* The frame has to render the page as Val sees it — draft content, with
|
|
16
|
+
* `data-val-path` on every element Val tracks — while *not* showing Val's own
|
|
17
|
+
* overlay, because the studio is already the UI. Nothing about the cookies can
|
|
18
|
+
* express that: they are per-origin, and the studio and the page share an
|
|
19
|
+
* origin, so a cookie would change the customer's own tabs too. A parameter on
|
|
20
|
+
* the frame's URL is scoped to exactly the one document that needs it.
|
|
21
|
+
*/
|
|
22
|
+
import type { ModuleFilePath, SourcePath } from "@valbuild/core";
|
|
23
|
+
/** Marks a page load as the studio's canvas frame. */
|
|
24
|
+
export declare const VAL_CANVAS_PARAM = "val_canvas";
|
|
25
|
+
/**
|
|
26
|
+
* Every message is tagged with this, and the tag is checked on receipt.
|
|
27
|
+
*
|
|
28
|
+
* A window receives messages from anything that has a handle on it —
|
|
29
|
+
* extensions, embeds, other frames — and `event.data` is whatever they chose to
|
|
30
|
+
* send. Checking a discriminator before reading anything else is what keeps a
|
|
31
|
+
* stray message from being read as a canvas instruction.
|
|
32
|
+
*/
|
|
33
|
+
export declare const VAL_CANVAS_MESSAGE = "val-canvas";
|
|
34
|
+
/** One element on the page that Val has a path for. */
|
|
35
|
+
export type ValCanvasElement = {
|
|
36
|
+
/**
|
|
37
|
+
* The paths on the element, already split.
|
|
38
|
+
*
|
|
39
|
+
* `data-val-path` holds a comma-separated list, because one element can
|
|
40
|
+
* render more than one piece of content — a heading built from two fields is
|
|
41
|
+
* one element with two paths.
|
|
42
|
+
*
|
|
43
|
+
* Typed as `SourcePath` rather than `string` because that is what they are:
|
|
44
|
+
* the page splits the attribute with Val's own splitter, so the values arrive
|
|
45
|
+
* already being paths. Saying so here is what keeps every reader of them from
|
|
46
|
+
* having to assert it again.
|
|
47
|
+
*/
|
|
48
|
+
paths: SourcePath[];
|
|
49
|
+
/** Where it is, in the page's own coordinates (unscaled, page-relative). */
|
|
50
|
+
rect: {
|
|
51
|
+
top: number;
|
|
52
|
+
left: number;
|
|
53
|
+
width: number;
|
|
54
|
+
height: number;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
/** What the page tells the studio. */
|
|
58
|
+
export type ValCanvasPageMessage = {
|
|
59
|
+
val: typeof VAL_CANVAS_MESSAGE;
|
|
60
|
+
type: "ready";
|
|
61
|
+
/**
|
|
62
|
+
* Whether the page is actually rendering draft content.
|
|
63
|
+
*
|
|
64
|
+
* The studio asks the server the same question, but the server answers
|
|
65
|
+
* for the *next* request; this answers for the document on screen, which
|
|
66
|
+
* is the one the person is looking at.
|
|
67
|
+
*/
|
|
68
|
+
draftMode: boolean;
|
|
69
|
+
/** The URL the frame ended up on, which a redirect may have changed. */
|
|
70
|
+
url: string;
|
|
71
|
+
} | {
|
|
72
|
+
val: typeof VAL_CANVAS_MESSAGE;
|
|
73
|
+
type: "elements";
|
|
74
|
+
elements: ValCanvasElement[];
|
|
75
|
+
/** The page's full layout size, for mapping a click back to an element. */
|
|
76
|
+
pageSize: {
|
|
77
|
+
width: number;
|
|
78
|
+
height: number;
|
|
79
|
+
};
|
|
80
|
+
} | {
|
|
81
|
+
val: typeof VAL_CANVAS_MESSAGE;
|
|
82
|
+
type: "clicked";
|
|
83
|
+
/** The paths on the element that was clicked. */
|
|
84
|
+
paths: SourcePath[];
|
|
85
|
+
} | {
|
|
86
|
+
val: typeof VAL_CANVAS_MESSAGE;
|
|
87
|
+
type: "refreshing";
|
|
88
|
+
/**
|
|
89
|
+
* Whether the page is re-rendering because of an edit.
|
|
90
|
+
*
|
|
91
|
+
* An edit reaches the page in two steps of very different cost: the
|
|
92
|
+
* client store updates at once, and anything rendered on the SERVER waits
|
|
93
|
+
* for a `router.refresh()` and its round trip — which in development is
|
|
94
|
+
* long enough to look like nothing happened. The studio cannot see that
|
|
95
|
+
* from outside, so the page says so and the canvas can show it is working.
|
|
96
|
+
*/
|
|
97
|
+
pending: boolean;
|
|
98
|
+
};
|
|
99
|
+
/** What the studio tells the page. */
|
|
100
|
+
export type ValCanvasStudioMessage = {
|
|
101
|
+
val: typeof VAL_CANVAS_MESSAGE;
|
|
102
|
+
type: "rescan";
|
|
103
|
+
} | {
|
|
104
|
+
val: typeof VAL_CANVAS_MESSAGE;
|
|
105
|
+
type: "highlight";
|
|
106
|
+
/** The path to outline, or null to clear the outline. */
|
|
107
|
+
path: SourcePath | null;
|
|
108
|
+
/** Bring it into view as well as outlining it. */
|
|
109
|
+
scrollIntoView?: boolean;
|
|
110
|
+
} | {
|
|
111
|
+
/**
|
|
112
|
+
* A module's source moved, so the page should re-render with it.
|
|
113
|
+
*
|
|
114
|
+
* The page already knows how to do this — it is what makes an inline edit
|
|
115
|
+
* visible behind the overlay — but it learns about it from a CustomEvent
|
|
116
|
+
* on its own window, and the studio is in a different window. So the
|
|
117
|
+
* update is relayed and the bridge re-dispatches it where the page is
|
|
118
|
+
* listening.
|
|
119
|
+
*
|
|
120
|
+
* Without it the canvas is only correct immediately after a load: a
|
|
121
|
+
* server component re-reads content when the page is requested, so
|
|
122
|
+
* everything typed since then is invisible until a reload.
|
|
123
|
+
*/
|
|
124
|
+
val: typeof VAL_CANVAS_MESSAGE;
|
|
125
|
+
type: "sourceUpdate";
|
|
126
|
+
moduleFilePath: ModuleFilePath;
|
|
127
|
+
/**
|
|
128
|
+
* The whole module source. `unknown` because the page's own types are the
|
|
129
|
+
* ones that give it meaning; nothing in between should be interpreting it.
|
|
130
|
+
*/
|
|
131
|
+
source: unknown;
|
|
132
|
+
} | {
|
|
133
|
+
/**
|
|
134
|
+
* Everything the studio holds has been sent.
|
|
135
|
+
*
|
|
136
|
+
* The page waits for draft sources before it renders, and it cannot know
|
|
137
|
+
* how many to expect. The studio only sends modules it has PATCHES for —
|
|
138
|
+
* an unedited module has no draft, so there is nothing to send — and the
|
|
139
|
+
* page cannot tell "not sent yet" from "nothing to send". Left to a
|
|
140
|
+
* timeout, every page reading an unedited module stalled ten seconds and
|
|
141
|
+
* then rendered committed source; the module that mattered, a router with
|
|
142
|
+
* a brand new page in it, was the one whose absence looked identical.
|
|
143
|
+
*
|
|
144
|
+
* So the studio says when it has finished. After this, a module the page
|
|
145
|
+
* has not been given has no draft, and committed source IS the draft.
|
|
146
|
+
*/
|
|
147
|
+
val: typeof VAL_CANVAS_MESSAGE;
|
|
148
|
+
type: "sourcesSynced";
|
|
149
|
+
} | {
|
|
150
|
+
val: typeof VAL_CANVAS_MESSAGE;
|
|
151
|
+
type: "setPicking";
|
|
152
|
+
/**
|
|
153
|
+
* Whether a click picks an element instead of doing what the page would
|
|
154
|
+
* do with it.
|
|
155
|
+
*
|
|
156
|
+
* Off by default: in the normal view the canvas is the page, and a click
|
|
157
|
+
* on a link should follow the link.
|
|
158
|
+
*/
|
|
159
|
+
picking: boolean;
|
|
160
|
+
};
|
|
161
|
+
/** Narrow an unknown `event.data` to a message from the page. */
|
|
162
|
+
export declare function isValCanvasPageMessage(data: unknown): data is ValCanvasPageMessage;
|
|
163
|
+
/** Narrow an unknown `event.data` to a message from the studio. */
|
|
164
|
+
export declare function isValCanvasStudioMessage(data: unknown): data is ValCanvasStudioMessage;
|
|
165
|
+
/**
|
|
166
|
+
* Whether this document was loaded as the studio's canvas frame.
|
|
167
|
+
*
|
|
168
|
+
* Reads the location directly rather than taking it as an argument, because
|
|
169
|
+
* every caller wants the answer for the document it is running in, and passing
|
|
170
|
+
* it around is how one of them ends up asking about the wrong one.
|
|
171
|
+
*/
|
|
172
|
+
export declare function isValCanvasFrame(search: string): boolean;
|
|
173
|
+
/** Add the canvas marker to a page URL. */
|
|
174
|
+
export declare function withValCanvasParam(url: string): string;
|
|
@@ -351,22 +351,21 @@ function convertImageNode(path, node, files, configDirectory, remoteOptions) {
|
|
|
351
351
|
}
|
|
352
352
|
return {
|
|
353
353
|
tag: "img",
|
|
354
|
-
src:
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
})
|
|
354
|
+
src: {
|
|
355
|
+
path: ref,
|
|
356
|
+
width: typeof node.attrs.width === "number" ? node.attrs.width : 0,
|
|
357
|
+
height: typeof node.attrs.height === "number" ? node.attrs.height : 0,
|
|
358
|
+
mimeType: mimeType
|
|
359
|
+
}
|
|
361
360
|
};
|
|
362
361
|
} else if (node.attrs) {
|
|
363
362
|
var url = node.attrs.src;
|
|
364
363
|
var patchId = getParam("patch_id", url);
|
|
364
|
+
// Recover the path the URL was built from. Local and remote produce the
|
|
365
|
+
// same source shape now, so this is only about undoing the URL rule.
|
|
365
366
|
var noParamsUrl = url.split("?")[0];
|
|
366
|
-
var remote = false;
|
|
367
367
|
if (patchId) {
|
|
368
|
-
|
|
369
|
-
if (remote) {
|
|
368
|
+
if (getParam("remote", url) === "true") {
|
|
370
369
|
var remoteRef = getParam("ref", url);
|
|
371
370
|
if (remoteRef) {
|
|
372
371
|
noParamsUrl = decodeURIComponent(remoteRef);
|
|
@@ -378,23 +377,18 @@ function convertImageNode(path, node, files, configDirectory, remoteOptions) {
|
|
|
378
377
|
} else {
|
|
379
378
|
console.error("Patched image URL does not start with /api/val/files: " + url);
|
|
380
379
|
}
|
|
380
|
+
} else if (remoteOptions && noParamsUrl.startsWith(remoteOptions.remoteHost)) ; else if (!noParamsUrl.startsWith("/public")) {
|
|
381
|
+
noParamsUrl = "/public".concat(noParamsUrl);
|
|
381
382
|
} else {
|
|
382
|
-
|
|
383
|
-
remote = true;
|
|
384
|
-
} else if (!noParamsUrl.startsWith("/public")) {
|
|
385
|
-
noParamsUrl = "/public".concat(noParamsUrl);
|
|
386
|
-
} else {
|
|
387
|
-
console.error("Unpatched image URL starts with /public: " + url);
|
|
388
|
-
}
|
|
383
|
+
console.error("Unpatched image URL starts with /public: " + url);
|
|
389
384
|
}
|
|
390
385
|
var tag = {
|
|
391
386
|
tag: "img",
|
|
392
|
-
src: _objectSpread2(
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
387
|
+
src: _objectSpread2({
|
|
388
|
+
path: noParamsUrl,
|
|
389
|
+
width: typeof node.attrs.width === "number" ? node.attrs.width : 0,
|
|
390
|
+
height: typeof node.attrs.height === "number" ? node.attrs.height : 0,
|
|
391
|
+
mimeType: noParamsUrl && core.Internal.filenameToMimeType(noParamsUrl) || ""
|
|
398
392
|
}, patchId ? {
|
|
399
393
|
patch_id: patchId
|
|
400
394
|
} : {})
|
|
@@ -675,24 +669,17 @@ function convertLinkNodeToRemirror(linkNode) {
|
|
|
675
669
|
});
|
|
676
670
|
}
|
|
677
671
|
function convertImageNodeToRemirror(imageNode) {
|
|
678
|
-
var
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
throw Error("Expected file source in image node");
|
|
682
|
-
}
|
|
683
|
-
var fileVal;
|
|
684
|
-
if (fileSource[core.VAL_EXTENSION] === "remote") {
|
|
685
|
-
fileVal = core.Internal.convertRemoteSource(fileSource);
|
|
686
|
-
} else {
|
|
687
|
-
fileVal = core.Internal.convertFileSource(fileSource);
|
|
672
|
+
var imageSource = imageNode.src;
|
|
673
|
+
if (typeof (imageSource === null || imageSource === void 0 ? void 0 : imageSource.path) !== "string") {
|
|
674
|
+
throw Error("Expected image source in image node");
|
|
688
675
|
}
|
|
689
676
|
return {
|
|
690
677
|
type: "image",
|
|
691
678
|
attrs: {
|
|
692
|
-
height:
|
|
693
|
-
width:
|
|
694
|
-
alt:
|
|
695
|
-
src:
|
|
679
|
+
height: imageSource.height,
|
|
680
|
+
width: imageSource.width,
|
|
681
|
+
alt: imageSource.alt,
|
|
682
|
+
src: core.Internal.mediaUrl(imageSource)
|
|
696
683
|
}
|
|
697
684
|
};
|
|
698
685
|
}
|
|
@@ -1014,6 +1001,14 @@ var SourcePath = zod.z.string().transform(function (path) {
|
|
|
1014
1001
|
|
|
1015
1002
|
var SerializedStringSchema = zod.z.object({
|
|
1016
1003
|
type: zod.z.literal("string"),
|
|
1004
|
+
// A render is static config, so unlike a `preview` it travels WHOLE — this
|
|
1005
|
+
// is the field the editor reads the layout from. See `core/src/render.ts`.
|
|
1006
|
+
render: zod.z.union([zod.z.object({
|
|
1007
|
+
as: zod.z.literal("textarea")
|
|
1008
|
+
}), zod.z.object({
|
|
1009
|
+
as: zod.z.literal("code"),
|
|
1010
|
+
language: zod.z["enum"](core.CODE_LANGUAGES)
|
|
1011
|
+
})]).optional(),
|
|
1017
1012
|
options: zod.z.object({
|
|
1018
1013
|
maxLength: zod.z.number().optional(),
|
|
1019
1014
|
minLength: zod.z.number().optional(),
|
|
@@ -1064,6 +1059,8 @@ var SerializedArraySchema = zod.z.lazy(function () {
|
|
|
1064
1059
|
type: zod.z.literal("array"),
|
|
1065
1060
|
item: SerializedSchema,
|
|
1066
1061
|
opt: zod.z["boolean"](),
|
|
1062
|
+
// Only whether a preview EXISTS: the closure itself cannot be serialized.
|
|
1063
|
+
preview: zod.z.literal(true).optional(),
|
|
1067
1064
|
readonly: zod.z["boolean"]().optional(),
|
|
1068
1065
|
hidden: zod.z["boolean"]().optional()
|
|
1069
1066
|
});
|
|
@@ -1133,6 +1130,8 @@ var SerializedRecordSchema = zod.z.lazy(function () {
|
|
|
1133
1130
|
type: zod.z.literal("record"),
|
|
1134
1131
|
item: SerializedSchema,
|
|
1135
1132
|
opt: zod.z["boolean"](),
|
|
1133
|
+
// Only whether a preview EXISTS: the closure itself cannot be serialized.
|
|
1134
|
+
preview: zod.z.literal(true).optional(),
|
|
1136
1135
|
// Optional gallery marker for files/images
|
|
1137
1136
|
mediaType: zod.z.union([zod.z.literal("files"), zod.z.literal("images")]).optional(),
|
|
1138
1137
|
// Optional legacy gallery metadata
|
|
@@ -1661,6 +1660,21 @@ var Api = {
|
|
|
1661
1660
|
schemaSha: zod.z.string(),
|
|
1662
1661
|
sourcesSha: zod.z.string(),
|
|
1663
1662
|
patches: zod.z.array(PatchId),
|
|
1663
|
+
/**
|
|
1664
|
+
* Unpublished changes the store threw away because it could not
|
|
1665
|
+
* read them.
|
|
1666
|
+
*
|
|
1667
|
+
* On stat rather than on `GET /patches` because stat is the
|
|
1668
|
+
* channel that always flows: the case worth reporting is a repair
|
|
1669
|
+
* that removed EVERYTHING, and then there is nothing left for the
|
|
1670
|
+
* studio to fetch, so a notice riding on the fetch is never
|
|
1671
|
+
* collected. Said once - the server drains it when it hands it
|
|
1672
|
+
* over.
|
|
1673
|
+
*/
|
|
1674
|
+
removed: zod.z.array(zod.z.object({
|
|
1675
|
+
patchId: PatchId,
|
|
1676
|
+
reason: zod.z.string()
|
|
1677
|
+
})).optional(),
|
|
1664
1678
|
config: ValConfig,
|
|
1665
1679
|
profileId: zod.z.string().nullable(),
|
|
1666
1680
|
mode: zod.z.union([zod.z.literal("http"), zod.z.literal("fs")]),
|
|
@@ -1907,7 +1921,7 @@ var Api = {
|
|
|
1907
1921
|
schemaSha: zod.z.string(),
|
|
1908
1922
|
sourcesSha: zod.z.string(),
|
|
1909
1923
|
modules: zod.z.record(ModuleFilePath, zod.z.object({
|
|
1910
|
-
|
|
1924
|
+
preview: zod.z.any().optional(),
|
|
1911
1925
|
// TODO: improve this type
|
|
1912
1926
|
source: zod.z.any().optional(),
|
|
1913
1927
|
//.optional(), // TODO: Json zod type
|
|
@@ -1987,7 +2001,23 @@ var Api = {
|
|
|
1987
2001
|
},
|
|
1988
2002
|
res: zod.z.union([unauthorizedResponse, zod.z.object({
|
|
1989
2003
|
status: zod.z.literal(200),
|
|
1990
|
-
json: zod.z.object({
|
|
2004
|
+
json: zod.z.object({
|
|
2005
|
+
/**
|
|
2006
|
+
* Unpublished changes the save threw away because they could not be
|
|
2007
|
+
* applied.
|
|
2008
|
+
*
|
|
2009
|
+
* `fs` mode only, and the reason the save is a 200 rather than the
|
|
2010
|
+
* 400 it used to be: with auto-save on, refusing the whole commit
|
|
2011
|
+
* for one bad patch means nothing is ever written again. The rest is
|
|
2012
|
+
* written, these are removed, and the studio drops them locally and
|
|
2013
|
+
* says so.
|
|
2014
|
+
*/
|
|
2015
|
+
removed: zod.z.array(zod.z.object({
|
|
2016
|
+
patchId: PatchId,
|
|
2017
|
+
moduleFilePath: ModuleFilePath,
|
|
2018
|
+
message: zod.z.string()
|
|
2019
|
+
})).optional()
|
|
2020
|
+
})
|
|
1991
2021
|
}), zod.z.object({
|
|
1992
2022
|
status: zod.z.literal(409),
|
|
1993
2023
|
json: zod.z.object({
|
|
@@ -3297,6 +3327,84 @@ function resolveSchemaSourceFixes(validationErrors, snapshot) {
|
|
|
3297
3327
|
return result;
|
|
3298
3328
|
}
|
|
3299
3329
|
|
|
3330
|
+
/**
|
|
3331
|
+
* The protocol between the studio's canvas and the page inside it.
|
|
3332
|
+
*
|
|
3333
|
+
* The canvas shows the running site in a frame. That frame is a different
|
|
3334
|
+
* document — the customer's app, rendered by their components — so the studio
|
|
3335
|
+
* cannot reach into it, and it cannot reach out. Everything they say to each
|
|
3336
|
+
* other goes through `postMessage`, and this is the vocabulary.
|
|
3337
|
+
*
|
|
3338
|
+
* It lives in `shared` because both halves have to agree on it: the page half
|
|
3339
|
+
* ships in `@valbuild/next`, the studio half in `@valbuild/ui`, and a message
|
|
3340
|
+
* shape defined twice is a message shape that will differ once.
|
|
3341
|
+
*
|
|
3342
|
+
* ## Why a query parameter turns it on
|
|
3343
|
+
*
|
|
3344
|
+
* The frame has to render the page as Val sees it — draft content, with
|
|
3345
|
+
* `data-val-path` on every element Val tracks — while *not* showing Val's own
|
|
3346
|
+
* overlay, because the studio is already the UI. Nothing about the cookies can
|
|
3347
|
+
* express that: they are per-origin, and the studio and the page share an
|
|
3348
|
+
* origin, so a cookie would change the customer's own tabs too. A parameter on
|
|
3349
|
+
* the frame's URL is scoped to exactly the one document that needs it.
|
|
3350
|
+
*/
|
|
3351
|
+
|
|
3352
|
+
/** Marks a page load as the studio's canvas frame. */
|
|
3353
|
+
var VAL_CANVAS_PARAM = "val_canvas";
|
|
3354
|
+
|
|
3355
|
+
/**
|
|
3356
|
+
* Every message is tagged with this, and the tag is checked on receipt.
|
|
3357
|
+
*
|
|
3358
|
+
* A window receives messages from anything that has a handle on it —
|
|
3359
|
+
* extensions, embeds, other frames — and `event.data` is whatever they chose to
|
|
3360
|
+
* send. Checking a discriminator before reading anything else is what keeps a
|
|
3361
|
+
* stray message from being read as a canvas instruction.
|
|
3362
|
+
*/
|
|
3363
|
+
var VAL_CANVAS_MESSAGE = "val-canvas";
|
|
3364
|
+
|
|
3365
|
+
/** One element on the page that Val has a path for. */
|
|
3366
|
+
|
|
3367
|
+
/** What the page tells the studio. */
|
|
3368
|
+
|
|
3369
|
+
/** What the studio tells the page. */
|
|
3370
|
+
|
|
3371
|
+
/** Narrow an unknown `event.data` to a message from the page. */
|
|
3372
|
+
function isValCanvasPageMessage(data) {
|
|
3373
|
+
if (_typeof(data) !== "object" || data === null) return false;
|
|
3374
|
+
var message = data;
|
|
3375
|
+
if (message.val !== VAL_CANVAS_MESSAGE) return false;
|
|
3376
|
+
return message.type === "ready" || message.type === "elements" || message.type === "clicked" || message.type === "refreshing";
|
|
3377
|
+
}
|
|
3378
|
+
|
|
3379
|
+
/** Narrow an unknown `event.data` to a message from the studio. */
|
|
3380
|
+
function isValCanvasStudioMessage(data) {
|
|
3381
|
+
if (_typeof(data) !== "object" || data === null) return false;
|
|
3382
|
+
var message = data;
|
|
3383
|
+
if (message.val !== VAL_CANVAS_MESSAGE) return false;
|
|
3384
|
+
return message.type === "rescan" || message.type === "highlight" || message.type === "setPicking" || message.type === "sourceUpdate" || message.type === "sourcesSynced";
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
/**
|
|
3388
|
+
* Whether this document was loaded as the studio's canvas frame.
|
|
3389
|
+
*
|
|
3390
|
+
* Reads the location directly rather than taking it as an argument, because
|
|
3391
|
+
* every caller wants the answer for the document it is running in, and passing
|
|
3392
|
+
* it around is how one of them ends up asking about the wrong one.
|
|
3393
|
+
*/
|
|
3394
|
+
function isValCanvasFrame(search) {
|
|
3395
|
+
try {
|
|
3396
|
+
return new URLSearchParams(search).get(VAL_CANVAS_PARAM) !== null;
|
|
3397
|
+
} catch (_unused) {
|
|
3398
|
+
return false;
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
/** Add the canvas marker to a page URL. */
|
|
3403
|
+
function withValCanvasParam(url) {
|
|
3404
|
+
var separator = url.includes("?") ? "&" : "?";
|
|
3405
|
+
return "".concat(url).concat(separator).concat(VAL_CANVAS_PARAM, "=1");
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3300
3408
|
exports.Api = Api;
|
|
3301
3409
|
exports.ColorOptions = ColorOptions;
|
|
3302
3410
|
exports.DEFAULT_ERROR_MESSAGE = DEFAULT_ERROR_MESSAGE;
|
|
@@ -3340,6 +3448,8 @@ exports.SerializedStringSchema = SerializedStringSchema;
|
|
|
3340
3448
|
exports.SerializedUnionSchema = SerializedUnionSchema;
|
|
3341
3449
|
exports.SharedValConfig = SharedValConfig;
|
|
3342
3450
|
exports.VAL_AI_SESSION_STORAGE_KEY = VAL_AI_SESSION_STORAGE_KEY;
|
|
3451
|
+
exports.VAL_CANVAS_MESSAGE = VAL_CANVAS_MESSAGE;
|
|
3452
|
+
exports.VAL_CANVAS_PARAM = VAL_CANVAS_PARAM;
|
|
3343
3453
|
exports.VAL_CONFIG_SESSION_STORAGE_KEY = VAL_CONFIG_SESSION_STORAGE_KEY;
|
|
3344
3454
|
exports.VAL_ENABLE_COOKIE_NAME = VAL_ENABLE_COOKIE_NAME;
|
|
3345
3455
|
exports.VAL_SESSION_COOKIE = VAL_SESSION_COOKIE;
|
|
@@ -3357,6 +3467,9 @@ exports.getNextAppRouterSourceFolder = getNextAppRouterSourceFolder;
|
|
|
3357
3467
|
exports.getPatternFromModuleFilePath = getPatternFromModuleFilePath;
|
|
3358
3468
|
exports.getRoutesOf = getRoutesOf;
|
|
3359
3469
|
exports.getRoutesWithModulePaths = getRoutesWithModulePaths;
|
|
3470
|
+
exports.isValCanvasFrame = isValCanvasFrame;
|
|
3471
|
+
exports.isValCanvasPageMessage = isValCanvasPageMessage;
|
|
3472
|
+
exports.isValCanvasStudioMessage = isValCanvasStudioMessage;
|
|
3360
3473
|
exports.levenshtein = levenshtein;
|
|
3361
3474
|
exports.parseRoutePattern = parseRoutePattern;
|
|
3362
3475
|
exports.remirrorToRichTextSource = remirrorToRichTextSource;
|
|
@@ -3365,3 +3478,4 @@ exports.resolveSchemaSourceFixes = resolveSchemaSourceFixes;
|
|
|
3365
3478
|
exports.richTextToRemirror = richTextToRemirror;
|
|
3366
3479
|
exports.urlOf = urlOf;
|
|
3367
3480
|
exports.validateRoutePatterns = validateRoutePatterns;
|
|
3481
|
+
exports.withValCanvasParam = withValCanvasParam;
|
|
@@ -351,22 +351,21 @@ function convertImageNode(path, node, files, configDirectory, remoteOptions) {
|
|
|
351
351
|
}
|
|
352
352
|
return {
|
|
353
353
|
tag: "img",
|
|
354
|
-
src:
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
})
|
|
354
|
+
src: {
|
|
355
|
+
path: ref,
|
|
356
|
+
width: typeof node.attrs.width === "number" ? node.attrs.width : 0,
|
|
357
|
+
height: typeof node.attrs.height === "number" ? node.attrs.height : 0,
|
|
358
|
+
mimeType: mimeType
|
|
359
|
+
}
|
|
361
360
|
};
|
|
362
361
|
} else if (node.attrs) {
|
|
363
362
|
var url = node.attrs.src;
|
|
364
363
|
var patchId = getParam("patch_id", url);
|
|
364
|
+
// Recover the path the URL was built from. Local and remote produce the
|
|
365
|
+
// same source shape now, so this is only about undoing the URL rule.
|
|
365
366
|
var noParamsUrl = url.split("?")[0];
|
|
366
|
-
var remote = false;
|
|
367
367
|
if (patchId) {
|
|
368
|
-
|
|
369
|
-
if (remote) {
|
|
368
|
+
if (getParam("remote", url) === "true") {
|
|
370
369
|
var remoteRef = getParam("ref", url);
|
|
371
370
|
if (remoteRef) {
|
|
372
371
|
noParamsUrl = decodeURIComponent(remoteRef);
|
|
@@ -378,23 +377,18 @@ function convertImageNode(path, node, files, configDirectory, remoteOptions) {
|
|
|
378
377
|
} else {
|
|
379
378
|
console.error("Patched image URL does not start with /api/val/files: " + url);
|
|
380
379
|
}
|
|
380
|
+
} else if (remoteOptions && noParamsUrl.startsWith(remoteOptions.remoteHost)) ; else if (!noParamsUrl.startsWith("/public")) {
|
|
381
|
+
noParamsUrl = "/public".concat(noParamsUrl);
|
|
381
382
|
} else {
|
|
382
|
-
|
|
383
|
-
remote = true;
|
|
384
|
-
} else if (!noParamsUrl.startsWith("/public")) {
|
|
385
|
-
noParamsUrl = "/public".concat(noParamsUrl);
|
|
386
|
-
} else {
|
|
387
|
-
console.error("Unpatched image URL starts with /public: " + url);
|
|
388
|
-
}
|
|
383
|
+
console.error("Unpatched image URL starts with /public: " + url);
|
|
389
384
|
}
|
|
390
385
|
var tag = {
|
|
391
386
|
tag: "img",
|
|
392
|
-
src: _objectSpread2(
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
387
|
+
src: _objectSpread2({
|
|
388
|
+
path: noParamsUrl,
|
|
389
|
+
width: typeof node.attrs.width === "number" ? node.attrs.width : 0,
|
|
390
|
+
height: typeof node.attrs.height === "number" ? node.attrs.height : 0,
|
|
391
|
+
mimeType: noParamsUrl && core.Internal.filenameToMimeType(noParamsUrl) || ""
|
|
398
392
|
}, patchId ? {
|
|
399
393
|
patch_id: patchId
|
|
400
394
|
} : {})
|
|
@@ -675,24 +669,17 @@ function convertLinkNodeToRemirror(linkNode) {
|
|
|
675
669
|
});
|
|
676
670
|
}
|
|
677
671
|
function convertImageNodeToRemirror(imageNode) {
|
|
678
|
-
var
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
throw Error("Expected file source in image node");
|
|
682
|
-
}
|
|
683
|
-
var fileVal;
|
|
684
|
-
if (fileSource[core.VAL_EXTENSION] === "remote") {
|
|
685
|
-
fileVal = core.Internal.convertRemoteSource(fileSource);
|
|
686
|
-
} else {
|
|
687
|
-
fileVal = core.Internal.convertFileSource(fileSource);
|
|
672
|
+
var imageSource = imageNode.src;
|
|
673
|
+
if (typeof (imageSource === null || imageSource === void 0 ? void 0 : imageSource.path) !== "string") {
|
|
674
|
+
throw Error("Expected image source in image node");
|
|
688
675
|
}
|
|
689
676
|
return {
|
|
690
677
|
type: "image",
|
|
691
678
|
attrs: {
|
|
692
|
-
height:
|
|
693
|
-
width:
|
|
694
|
-
alt:
|
|
695
|
-
src:
|
|
679
|
+
height: imageSource.height,
|
|
680
|
+
width: imageSource.width,
|
|
681
|
+
alt: imageSource.alt,
|
|
682
|
+
src: core.Internal.mediaUrl(imageSource)
|
|
696
683
|
}
|
|
697
684
|
};
|
|
698
685
|
}
|
|
@@ -1014,6 +1001,14 @@ var SourcePath = zod.z.string().transform(function (path) {
|
|
|
1014
1001
|
|
|
1015
1002
|
var SerializedStringSchema = zod.z.object({
|
|
1016
1003
|
type: zod.z.literal("string"),
|
|
1004
|
+
// A render is static config, so unlike a `preview` it travels WHOLE — this
|
|
1005
|
+
// is the field the editor reads the layout from. See `core/src/render.ts`.
|
|
1006
|
+
render: zod.z.union([zod.z.object({
|
|
1007
|
+
as: zod.z.literal("textarea")
|
|
1008
|
+
}), zod.z.object({
|
|
1009
|
+
as: zod.z.literal("code"),
|
|
1010
|
+
language: zod.z["enum"](core.CODE_LANGUAGES)
|
|
1011
|
+
})]).optional(),
|
|
1017
1012
|
options: zod.z.object({
|
|
1018
1013
|
maxLength: zod.z.number().optional(),
|
|
1019
1014
|
minLength: zod.z.number().optional(),
|
|
@@ -1064,6 +1059,8 @@ var SerializedArraySchema = zod.z.lazy(function () {
|
|
|
1064
1059
|
type: zod.z.literal("array"),
|
|
1065
1060
|
item: SerializedSchema,
|
|
1066
1061
|
opt: zod.z["boolean"](),
|
|
1062
|
+
// Only whether a preview EXISTS: the closure itself cannot be serialized.
|
|
1063
|
+
preview: zod.z.literal(true).optional(),
|
|
1067
1064
|
readonly: zod.z["boolean"]().optional(),
|
|
1068
1065
|
hidden: zod.z["boolean"]().optional()
|
|
1069
1066
|
});
|
|
@@ -1133,6 +1130,8 @@ var SerializedRecordSchema = zod.z.lazy(function () {
|
|
|
1133
1130
|
type: zod.z.literal("record"),
|
|
1134
1131
|
item: SerializedSchema,
|
|
1135
1132
|
opt: zod.z["boolean"](),
|
|
1133
|
+
// Only whether a preview EXISTS: the closure itself cannot be serialized.
|
|
1134
|
+
preview: zod.z.literal(true).optional(),
|
|
1136
1135
|
// Optional gallery marker for files/images
|
|
1137
1136
|
mediaType: zod.z.union([zod.z.literal("files"), zod.z.literal("images")]).optional(),
|
|
1138
1137
|
// Optional legacy gallery metadata
|
|
@@ -1661,6 +1660,21 @@ var Api = {
|
|
|
1661
1660
|
schemaSha: zod.z.string(),
|
|
1662
1661
|
sourcesSha: zod.z.string(),
|
|
1663
1662
|
patches: zod.z.array(PatchId),
|
|
1663
|
+
/**
|
|
1664
|
+
* Unpublished changes the store threw away because it could not
|
|
1665
|
+
* read them.
|
|
1666
|
+
*
|
|
1667
|
+
* On stat rather than on `GET /patches` because stat is the
|
|
1668
|
+
* channel that always flows: the case worth reporting is a repair
|
|
1669
|
+
* that removed EVERYTHING, and then there is nothing left for the
|
|
1670
|
+
* studio to fetch, so a notice riding on the fetch is never
|
|
1671
|
+
* collected. Said once - the server drains it when it hands it
|
|
1672
|
+
* over.
|
|
1673
|
+
*/
|
|
1674
|
+
removed: zod.z.array(zod.z.object({
|
|
1675
|
+
patchId: PatchId,
|
|
1676
|
+
reason: zod.z.string()
|
|
1677
|
+
})).optional(),
|
|
1664
1678
|
config: ValConfig,
|
|
1665
1679
|
profileId: zod.z.string().nullable(),
|
|
1666
1680
|
mode: zod.z.union([zod.z.literal("http"), zod.z.literal("fs")]),
|
|
@@ -1907,7 +1921,7 @@ var Api = {
|
|
|
1907
1921
|
schemaSha: zod.z.string(),
|
|
1908
1922
|
sourcesSha: zod.z.string(),
|
|
1909
1923
|
modules: zod.z.record(ModuleFilePath, zod.z.object({
|
|
1910
|
-
|
|
1924
|
+
preview: zod.z.any().optional(),
|
|
1911
1925
|
// TODO: improve this type
|
|
1912
1926
|
source: zod.z.any().optional(),
|
|
1913
1927
|
//.optional(), // TODO: Json zod type
|
|
@@ -1987,7 +2001,23 @@ var Api = {
|
|
|
1987
2001
|
},
|
|
1988
2002
|
res: zod.z.union([unauthorizedResponse, zod.z.object({
|
|
1989
2003
|
status: zod.z.literal(200),
|
|
1990
|
-
json: zod.z.object({
|
|
2004
|
+
json: zod.z.object({
|
|
2005
|
+
/**
|
|
2006
|
+
* Unpublished changes the save threw away because they could not be
|
|
2007
|
+
* applied.
|
|
2008
|
+
*
|
|
2009
|
+
* `fs` mode only, and the reason the save is a 200 rather than the
|
|
2010
|
+
* 400 it used to be: with auto-save on, refusing the whole commit
|
|
2011
|
+
* for one bad patch means nothing is ever written again. The rest is
|
|
2012
|
+
* written, these are removed, and the studio drops them locally and
|
|
2013
|
+
* says so.
|
|
2014
|
+
*/
|
|
2015
|
+
removed: zod.z.array(zod.z.object({
|
|
2016
|
+
patchId: PatchId,
|
|
2017
|
+
moduleFilePath: ModuleFilePath,
|
|
2018
|
+
message: zod.z.string()
|
|
2019
|
+
})).optional()
|
|
2020
|
+
})
|
|
1991
2021
|
}), zod.z.object({
|
|
1992
2022
|
status: zod.z.literal(409),
|
|
1993
2023
|
json: zod.z.object({
|
|
@@ -3297,6 +3327,84 @@ function resolveSchemaSourceFixes(validationErrors, snapshot) {
|
|
|
3297
3327
|
return result;
|
|
3298
3328
|
}
|
|
3299
3329
|
|
|
3330
|
+
/**
|
|
3331
|
+
* The protocol between the studio's canvas and the page inside it.
|
|
3332
|
+
*
|
|
3333
|
+
* The canvas shows the running site in a frame. That frame is a different
|
|
3334
|
+
* document — the customer's app, rendered by their components — so the studio
|
|
3335
|
+
* cannot reach into it, and it cannot reach out. Everything they say to each
|
|
3336
|
+
* other goes through `postMessage`, and this is the vocabulary.
|
|
3337
|
+
*
|
|
3338
|
+
* It lives in `shared` because both halves have to agree on it: the page half
|
|
3339
|
+
* ships in `@valbuild/next`, the studio half in `@valbuild/ui`, and a message
|
|
3340
|
+
* shape defined twice is a message shape that will differ once.
|
|
3341
|
+
*
|
|
3342
|
+
* ## Why a query parameter turns it on
|
|
3343
|
+
*
|
|
3344
|
+
* The frame has to render the page as Val sees it — draft content, with
|
|
3345
|
+
* `data-val-path` on every element Val tracks — while *not* showing Val's own
|
|
3346
|
+
* overlay, because the studio is already the UI. Nothing about the cookies can
|
|
3347
|
+
* express that: they are per-origin, and the studio and the page share an
|
|
3348
|
+
* origin, so a cookie would change the customer's own tabs too. A parameter on
|
|
3349
|
+
* the frame's URL is scoped to exactly the one document that needs it.
|
|
3350
|
+
*/
|
|
3351
|
+
|
|
3352
|
+
/** Marks a page load as the studio's canvas frame. */
|
|
3353
|
+
var VAL_CANVAS_PARAM = "val_canvas";
|
|
3354
|
+
|
|
3355
|
+
/**
|
|
3356
|
+
* Every message is tagged with this, and the tag is checked on receipt.
|
|
3357
|
+
*
|
|
3358
|
+
* A window receives messages from anything that has a handle on it —
|
|
3359
|
+
* extensions, embeds, other frames — and `event.data` is whatever they chose to
|
|
3360
|
+
* send. Checking a discriminator before reading anything else is what keeps a
|
|
3361
|
+
* stray message from being read as a canvas instruction.
|
|
3362
|
+
*/
|
|
3363
|
+
var VAL_CANVAS_MESSAGE = "val-canvas";
|
|
3364
|
+
|
|
3365
|
+
/** One element on the page that Val has a path for. */
|
|
3366
|
+
|
|
3367
|
+
/** What the page tells the studio. */
|
|
3368
|
+
|
|
3369
|
+
/** What the studio tells the page. */
|
|
3370
|
+
|
|
3371
|
+
/** Narrow an unknown `event.data` to a message from the page. */
|
|
3372
|
+
function isValCanvasPageMessage(data) {
|
|
3373
|
+
if (_typeof(data) !== "object" || data === null) return false;
|
|
3374
|
+
var message = data;
|
|
3375
|
+
if (message.val !== VAL_CANVAS_MESSAGE) return false;
|
|
3376
|
+
return message.type === "ready" || message.type === "elements" || message.type === "clicked" || message.type === "refreshing";
|
|
3377
|
+
}
|
|
3378
|
+
|
|
3379
|
+
/** Narrow an unknown `event.data` to a message from the studio. */
|
|
3380
|
+
function isValCanvasStudioMessage(data) {
|
|
3381
|
+
if (_typeof(data) !== "object" || data === null) return false;
|
|
3382
|
+
var message = data;
|
|
3383
|
+
if (message.val !== VAL_CANVAS_MESSAGE) return false;
|
|
3384
|
+
return message.type === "rescan" || message.type === "highlight" || message.type === "setPicking" || message.type === "sourceUpdate" || message.type === "sourcesSynced";
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
/**
|
|
3388
|
+
* Whether this document was loaded as the studio's canvas frame.
|
|
3389
|
+
*
|
|
3390
|
+
* Reads the location directly rather than taking it as an argument, because
|
|
3391
|
+
* every caller wants the answer for the document it is running in, and passing
|
|
3392
|
+
* it around is how one of them ends up asking about the wrong one.
|
|
3393
|
+
*/
|
|
3394
|
+
function isValCanvasFrame(search) {
|
|
3395
|
+
try {
|
|
3396
|
+
return new URLSearchParams(search).get(VAL_CANVAS_PARAM) !== null;
|
|
3397
|
+
} catch (_unused) {
|
|
3398
|
+
return false;
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
/** Add the canvas marker to a page URL. */
|
|
3403
|
+
function withValCanvasParam(url) {
|
|
3404
|
+
var separator = url.includes("?") ? "&" : "?";
|
|
3405
|
+
return "".concat(url).concat(separator).concat(VAL_CANVAS_PARAM, "=1");
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3300
3408
|
exports.Api = Api;
|
|
3301
3409
|
exports.ColorOptions = ColorOptions;
|
|
3302
3410
|
exports.DEFAULT_ERROR_MESSAGE = DEFAULT_ERROR_MESSAGE;
|
|
@@ -3340,6 +3448,8 @@ exports.SerializedStringSchema = SerializedStringSchema;
|
|
|
3340
3448
|
exports.SerializedUnionSchema = SerializedUnionSchema;
|
|
3341
3449
|
exports.SharedValConfig = SharedValConfig;
|
|
3342
3450
|
exports.VAL_AI_SESSION_STORAGE_KEY = VAL_AI_SESSION_STORAGE_KEY;
|
|
3451
|
+
exports.VAL_CANVAS_MESSAGE = VAL_CANVAS_MESSAGE;
|
|
3452
|
+
exports.VAL_CANVAS_PARAM = VAL_CANVAS_PARAM;
|
|
3343
3453
|
exports.VAL_CONFIG_SESSION_STORAGE_KEY = VAL_CONFIG_SESSION_STORAGE_KEY;
|
|
3344
3454
|
exports.VAL_ENABLE_COOKIE_NAME = VAL_ENABLE_COOKIE_NAME;
|
|
3345
3455
|
exports.VAL_SESSION_COOKIE = VAL_SESSION_COOKIE;
|
|
@@ -3357,6 +3467,9 @@ exports.getNextAppRouterSourceFolder = getNextAppRouterSourceFolder;
|
|
|
3357
3467
|
exports.getPatternFromModuleFilePath = getPatternFromModuleFilePath;
|
|
3358
3468
|
exports.getRoutesOf = getRoutesOf;
|
|
3359
3469
|
exports.getRoutesWithModulePaths = getRoutesWithModulePaths;
|
|
3470
|
+
exports.isValCanvasFrame = isValCanvasFrame;
|
|
3471
|
+
exports.isValCanvasPageMessage = isValCanvasPageMessage;
|
|
3472
|
+
exports.isValCanvasStudioMessage = isValCanvasStudioMessage;
|
|
3360
3473
|
exports.levenshtein = levenshtein;
|
|
3361
3474
|
exports.parseRoutePattern = parseRoutePattern;
|
|
3362
3475
|
exports.remirrorToRichTextSource = remirrorToRichTextSource;
|
|
@@ -3365,3 +3478,4 @@ exports.resolveSchemaSourceFixes = resolveSchemaSourceFixes;
|
|
|
3365
3478
|
exports.richTextToRemirror = richTextToRemirror;
|
|
3366
3479
|
exports.urlOf = urlOf;
|
|
3367
3480
|
exports.validateRoutePatterns = validateRoutePatterns;
|
|
3481
|
+
exports.withValCanvasParam = withValCanvasParam;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Internal,
|
|
1
|
+
import { Internal, CODE_LANGUAGES } from '@valbuild/core';
|
|
2
2
|
import { b as base64DataUrlToUint8Array } from '../../dist/utils-00f3e1df.esm.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { fromError } from 'zod-validation-error';
|
|
@@ -347,22 +347,21 @@ function convertImageNode(path, node, files, configDirectory, remoteOptions) {
|
|
|
347
347
|
}
|
|
348
348
|
return {
|
|
349
349
|
tag: "img",
|
|
350
|
-
src:
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
})
|
|
350
|
+
src: {
|
|
351
|
+
path: ref,
|
|
352
|
+
width: typeof node.attrs.width === "number" ? node.attrs.width : 0,
|
|
353
|
+
height: typeof node.attrs.height === "number" ? node.attrs.height : 0,
|
|
354
|
+
mimeType: mimeType
|
|
355
|
+
}
|
|
357
356
|
};
|
|
358
357
|
} else if (node.attrs) {
|
|
359
358
|
var url = node.attrs.src;
|
|
360
359
|
var patchId = getParam("patch_id", url);
|
|
360
|
+
// Recover the path the URL was built from. Local and remote produce the
|
|
361
|
+
// same source shape now, so this is only about undoing the URL rule.
|
|
361
362
|
var noParamsUrl = url.split("?")[0];
|
|
362
|
-
var remote = false;
|
|
363
363
|
if (patchId) {
|
|
364
|
-
|
|
365
|
-
if (remote) {
|
|
364
|
+
if (getParam("remote", url) === "true") {
|
|
366
365
|
var remoteRef = getParam("ref", url);
|
|
367
366
|
if (remoteRef) {
|
|
368
367
|
noParamsUrl = decodeURIComponent(remoteRef);
|
|
@@ -374,23 +373,18 @@ function convertImageNode(path, node, files, configDirectory, remoteOptions) {
|
|
|
374
373
|
} else {
|
|
375
374
|
console.error("Patched image URL does not start with /api/val/files: " + url);
|
|
376
375
|
}
|
|
376
|
+
} else if (remoteOptions && noParamsUrl.startsWith(remoteOptions.remoteHost)) ; else if (!noParamsUrl.startsWith("/public")) {
|
|
377
|
+
noParamsUrl = "/public".concat(noParamsUrl);
|
|
377
378
|
} else {
|
|
378
|
-
|
|
379
|
-
remote = true;
|
|
380
|
-
} else if (!noParamsUrl.startsWith("/public")) {
|
|
381
|
-
noParamsUrl = "/public".concat(noParamsUrl);
|
|
382
|
-
} else {
|
|
383
|
-
console.error("Unpatched image URL starts with /public: " + url);
|
|
384
|
-
}
|
|
379
|
+
console.error("Unpatched image URL starts with /public: " + url);
|
|
385
380
|
}
|
|
386
381
|
var tag = {
|
|
387
382
|
tag: "img",
|
|
388
|
-
src: _objectSpread2(
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
383
|
+
src: _objectSpread2({
|
|
384
|
+
path: noParamsUrl,
|
|
385
|
+
width: typeof node.attrs.width === "number" ? node.attrs.width : 0,
|
|
386
|
+
height: typeof node.attrs.height === "number" ? node.attrs.height : 0,
|
|
387
|
+
mimeType: noParamsUrl && Internal.filenameToMimeType(noParamsUrl) || ""
|
|
394
388
|
}, patchId ? {
|
|
395
389
|
patch_id: patchId
|
|
396
390
|
} : {})
|
|
@@ -671,24 +665,17 @@ function convertLinkNodeToRemirror(linkNode) {
|
|
|
671
665
|
});
|
|
672
666
|
}
|
|
673
667
|
function convertImageNodeToRemirror(imageNode) {
|
|
674
|
-
var
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
throw Error("Expected file source in image node");
|
|
678
|
-
}
|
|
679
|
-
var fileVal;
|
|
680
|
-
if (fileSource[VAL_EXTENSION] === "remote") {
|
|
681
|
-
fileVal = Internal.convertRemoteSource(fileSource);
|
|
682
|
-
} else {
|
|
683
|
-
fileVal = Internal.convertFileSource(fileSource);
|
|
668
|
+
var imageSource = imageNode.src;
|
|
669
|
+
if (typeof (imageSource === null || imageSource === void 0 ? void 0 : imageSource.path) !== "string") {
|
|
670
|
+
throw Error("Expected image source in image node");
|
|
684
671
|
}
|
|
685
672
|
return {
|
|
686
673
|
type: "image",
|
|
687
674
|
attrs: {
|
|
688
|
-
height:
|
|
689
|
-
width:
|
|
690
|
-
alt:
|
|
691
|
-
src:
|
|
675
|
+
height: imageSource.height,
|
|
676
|
+
width: imageSource.width,
|
|
677
|
+
alt: imageSource.alt,
|
|
678
|
+
src: Internal.mediaUrl(imageSource)
|
|
692
679
|
}
|
|
693
680
|
};
|
|
694
681
|
}
|
|
@@ -1010,6 +997,14 @@ var SourcePath = z.string().transform(function (path) {
|
|
|
1010
997
|
|
|
1011
998
|
var SerializedStringSchema = z.object({
|
|
1012
999
|
type: z.literal("string"),
|
|
1000
|
+
// A render is static config, so unlike a `preview` it travels WHOLE — this
|
|
1001
|
+
// is the field the editor reads the layout from. See `core/src/render.ts`.
|
|
1002
|
+
render: z.union([z.object({
|
|
1003
|
+
as: z.literal("textarea")
|
|
1004
|
+
}), z.object({
|
|
1005
|
+
as: z.literal("code"),
|
|
1006
|
+
language: z["enum"](CODE_LANGUAGES)
|
|
1007
|
+
})]).optional(),
|
|
1013
1008
|
options: z.object({
|
|
1014
1009
|
maxLength: z.number().optional(),
|
|
1015
1010
|
minLength: z.number().optional(),
|
|
@@ -1060,6 +1055,8 @@ var SerializedArraySchema = z.lazy(function () {
|
|
|
1060
1055
|
type: z.literal("array"),
|
|
1061
1056
|
item: SerializedSchema,
|
|
1062
1057
|
opt: z["boolean"](),
|
|
1058
|
+
// Only whether a preview EXISTS: the closure itself cannot be serialized.
|
|
1059
|
+
preview: z.literal(true).optional(),
|
|
1063
1060
|
readonly: z["boolean"]().optional(),
|
|
1064
1061
|
hidden: z["boolean"]().optional()
|
|
1065
1062
|
});
|
|
@@ -1129,6 +1126,8 @@ var SerializedRecordSchema = z.lazy(function () {
|
|
|
1129
1126
|
type: z.literal("record"),
|
|
1130
1127
|
item: SerializedSchema,
|
|
1131
1128
|
opt: z["boolean"](),
|
|
1129
|
+
// Only whether a preview EXISTS: the closure itself cannot be serialized.
|
|
1130
|
+
preview: z.literal(true).optional(),
|
|
1132
1131
|
// Optional gallery marker for files/images
|
|
1133
1132
|
mediaType: z.union([z.literal("files"), z.literal("images")]).optional(),
|
|
1134
1133
|
// Optional legacy gallery metadata
|
|
@@ -1657,6 +1656,21 @@ var Api = {
|
|
|
1657
1656
|
schemaSha: z.string(),
|
|
1658
1657
|
sourcesSha: z.string(),
|
|
1659
1658
|
patches: z.array(PatchId),
|
|
1659
|
+
/**
|
|
1660
|
+
* Unpublished changes the store threw away because it could not
|
|
1661
|
+
* read them.
|
|
1662
|
+
*
|
|
1663
|
+
* On stat rather than on `GET /patches` because stat is the
|
|
1664
|
+
* channel that always flows: the case worth reporting is a repair
|
|
1665
|
+
* that removed EVERYTHING, and then there is nothing left for the
|
|
1666
|
+
* studio to fetch, so a notice riding on the fetch is never
|
|
1667
|
+
* collected. Said once - the server drains it when it hands it
|
|
1668
|
+
* over.
|
|
1669
|
+
*/
|
|
1670
|
+
removed: z.array(z.object({
|
|
1671
|
+
patchId: PatchId,
|
|
1672
|
+
reason: z.string()
|
|
1673
|
+
})).optional(),
|
|
1660
1674
|
config: ValConfig,
|
|
1661
1675
|
profileId: z.string().nullable(),
|
|
1662
1676
|
mode: z.union([z.literal("http"), z.literal("fs")]),
|
|
@@ -1903,7 +1917,7 @@ var Api = {
|
|
|
1903
1917
|
schemaSha: z.string(),
|
|
1904
1918
|
sourcesSha: z.string(),
|
|
1905
1919
|
modules: z.record(ModuleFilePath, z.object({
|
|
1906
|
-
|
|
1920
|
+
preview: z.any().optional(),
|
|
1907
1921
|
// TODO: improve this type
|
|
1908
1922
|
source: z.any().optional(),
|
|
1909
1923
|
//.optional(), // TODO: Json zod type
|
|
@@ -1983,7 +1997,23 @@ var Api = {
|
|
|
1983
1997
|
},
|
|
1984
1998
|
res: z.union([unauthorizedResponse, z.object({
|
|
1985
1999
|
status: z.literal(200),
|
|
1986
|
-
json: z.object({
|
|
2000
|
+
json: z.object({
|
|
2001
|
+
/**
|
|
2002
|
+
* Unpublished changes the save threw away because they could not be
|
|
2003
|
+
* applied.
|
|
2004
|
+
*
|
|
2005
|
+
* `fs` mode only, and the reason the save is a 200 rather than the
|
|
2006
|
+
* 400 it used to be: with auto-save on, refusing the whole commit
|
|
2007
|
+
* for one bad patch means nothing is ever written again. The rest is
|
|
2008
|
+
* written, these are removed, and the studio drops them locally and
|
|
2009
|
+
* says so.
|
|
2010
|
+
*/
|
|
2011
|
+
removed: z.array(z.object({
|
|
2012
|
+
patchId: PatchId,
|
|
2013
|
+
moduleFilePath: ModuleFilePath,
|
|
2014
|
+
message: z.string()
|
|
2015
|
+
})).optional()
|
|
2016
|
+
})
|
|
1987
2017
|
}), z.object({
|
|
1988
2018
|
status: z.literal(409),
|
|
1989
2019
|
json: z.object({
|
|
@@ -3293,4 +3323,82 @@ function resolveSchemaSourceFixes(validationErrors, snapshot) {
|
|
|
3293
3323
|
return result;
|
|
3294
3324
|
}
|
|
3295
3325
|
|
|
3296
|
-
|
|
3326
|
+
/**
|
|
3327
|
+
* The protocol between the studio's canvas and the page inside it.
|
|
3328
|
+
*
|
|
3329
|
+
* The canvas shows the running site in a frame. That frame is a different
|
|
3330
|
+
* document — the customer's app, rendered by their components — so the studio
|
|
3331
|
+
* cannot reach into it, and it cannot reach out. Everything they say to each
|
|
3332
|
+
* other goes through `postMessage`, and this is the vocabulary.
|
|
3333
|
+
*
|
|
3334
|
+
* It lives in `shared` because both halves have to agree on it: the page half
|
|
3335
|
+
* ships in `@valbuild/next`, the studio half in `@valbuild/ui`, and a message
|
|
3336
|
+
* shape defined twice is a message shape that will differ once.
|
|
3337
|
+
*
|
|
3338
|
+
* ## Why a query parameter turns it on
|
|
3339
|
+
*
|
|
3340
|
+
* The frame has to render the page as Val sees it — draft content, with
|
|
3341
|
+
* `data-val-path` on every element Val tracks — while *not* showing Val's own
|
|
3342
|
+
* overlay, because the studio is already the UI. Nothing about the cookies can
|
|
3343
|
+
* express that: they are per-origin, and the studio and the page share an
|
|
3344
|
+
* origin, so a cookie would change the customer's own tabs too. A parameter on
|
|
3345
|
+
* the frame's URL is scoped to exactly the one document that needs it.
|
|
3346
|
+
*/
|
|
3347
|
+
|
|
3348
|
+
/** Marks a page load as the studio's canvas frame. */
|
|
3349
|
+
var VAL_CANVAS_PARAM = "val_canvas";
|
|
3350
|
+
|
|
3351
|
+
/**
|
|
3352
|
+
* Every message is tagged with this, and the tag is checked on receipt.
|
|
3353
|
+
*
|
|
3354
|
+
* A window receives messages from anything that has a handle on it —
|
|
3355
|
+
* extensions, embeds, other frames — and `event.data` is whatever they chose to
|
|
3356
|
+
* send. Checking a discriminator before reading anything else is what keeps a
|
|
3357
|
+
* stray message from being read as a canvas instruction.
|
|
3358
|
+
*/
|
|
3359
|
+
var VAL_CANVAS_MESSAGE = "val-canvas";
|
|
3360
|
+
|
|
3361
|
+
/** One element on the page that Val has a path for. */
|
|
3362
|
+
|
|
3363
|
+
/** What the page tells the studio. */
|
|
3364
|
+
|
|
3365
|
+
/** What the studio tells the page. */
|
|
3366
|
+
|
|
3367
|
+
/** Narrow an unknown `event.data` to a message from the page. */
|
|
3368
|
+
function isValCanvasPageMessage(data) {
|
|
3369
|
+
if (_typeof(data) !== "object" || data === null) return false;
|
|
3370
|
+
var message = data;
|
|
3371
|
+
if (message.val !== VAL_CANVAS_MESSAGE) return false;
|
|
3372
|
+
return message.type === "ready" || message.type === "elements" || message.type === "clicked" || message.type === "refreshing";
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3375
|
+
/** Narrow an unknown `event.data` to a message from the studio. */
|
|
3376
|
+
function isValCanvasStudioMessage(data) {
|
|
3377
|
+
if (_typeof(data) !== "object" || data === null) return false;
|
|
3378
|
+
var message = data;
|
|
3379
|
+
if (message.val !== VAL_CANVAS_MESSAGE) return false;
|
|
3380
|
+
return message.type === "rescan" || message.type === "highlight" || message.type === "setPicking" || message.type === "sourceUpdate" || message.type === "sourcesSynced";
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
/**
|
|
3384
|
+
* Whether this document was loaded as the studio's canvas frame.
|
|
3385
|
+
*
|
|
3386
|
+
* Reads the location directly rather than taking it as an argument, because
|
|
3387
|
+
* every caller wants the answer for the document it is running in, and passing
|
|
3388
|
+
* it around is how one of them ends up asking about the wrong one.
|
|
3389
|
+
*/
|
|
3390
|
+
function isValCanvasFrame(search) {
|
|
3391
|
+
try {
|
|
3392
|
+
return new URLSearchParams(search).get(VAL_CANVAS_PARAM) !== null;
|
|
3393
|
+
} catch (_unused) {
|
|
3394
|
+
return false;
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
|
|
3398
|
+
/** Add the canvas marker to a page URL. */
|
|
3399
|
+
function withValCanvasParam(url) {
|
|
3400
|
+
var separator = url.includes("?") ? "&" : "?";
|
|
3401
|
+
return "".concat(url).concat(separator).concat(VAL_CANVAS_PARAM, "=1");
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
export { Api, ColorOptions, DEFAULT_ERROR_MESSAGE, DateOptions, FileOptions, ImageOptions, JSON_ENTRIES_BATCH_MAX, ParentRef$1 as ParentRef, Patch, PatchBlock, PatchId, PatchJSON, RemirrorBr, RemirrorBulletList, RemirrorHeading, RemirrorImage, RemirrorJSON, RemirrorLinkMark, RemirrorListItem, RemirrorOrderedList, RemirrorParagraph, RemirrorText, RemirrorTextMark, RichTextOptions, SerializedArraySchema, SerializedBooleanSchema, SerializedColorSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedFileSchema, SerializedImageSchema, SerializedKeyOfSchema, SerializedLiteralSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedRecordSchema, SerializedRichTextSchema, SerializedRouteSchema, SerializedSchema, SerializedStringSchema, SerializedUnionSchema, SharedValConfig, VAL_AI_SESSION_STORAGE_KEY, VAL_CANVAS_MESSAGE, VAL_CANVAS_PARAM, VAL_CONFIG_SESSION_STORAGE_KEY, VAL_ENABLE_COOKIE_NAME, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, VAL_THEME_SESSION_STORAGE_KEY, ValCommit, ValDeployment, createRegExpFromPattern, createValClient, filterRoutesByPatterns, findSimilar, getErrorMessageFromUnknownJson, getNextAppRouterSitemapTree, getNextAppRouterSourceFolder, getPatternFromModuleFilePath, getRoutesOf, getRoutesWithModulePaths, isValCanvasFrame, isValCanvasPageMessage, isValCanvasStudioMessage, levenshtein, parseRoutePattern, remirrorToRichTextSource, resolveSchemaSourceFixForError, resolveSchemaSourceFixes, richTextToRemirror, urlOf, validateRoutePatterns, withValCanvasParam };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@valbuild/shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.105.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Val shared types and utilities",
|
|
6
6
|
"repository": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"zod": "^4.3.5",
|
|
33
33
|
"zod-validation-error": "^5.0.0",
|
|
34
|
-
"@valbuild/core": "0.
|
|
34
|
+
"@valbuild/core": "0.105.0"
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
37
|
"dist",
|