@picked/client 0.0.70-next.1 → 0.0.70-next.2
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/{api-RrCWPX5t.js → api-6xq5wVtp.js} +1 -1
- package/dist/{api-CHKRKQ_I.cjs → api-CGaBTtfd.cjs} +1 -1
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
- package/dist/cjs/preview/index.js +1 -0
- package/dist/cjs/server/index.js +1 -1
- package/dist/esm/index.js +243 -211
- package/dist/esm/plugins/dynamic-experiences/index.js +2 -2
- package/dist/esm/preview/index.js +2 -0
- package/dist/esm/server/index.js +1 -1
- package/dist/inert-script-report-B_bMPRzp.js +18 -0
- package/dist/inert-script-report-ijRHPtcY.cjs +1 -0
- package/dist/{plugin-DF3_YOaU.js → plugin-GGRZMJr7.js} +3 -3
- package/dist/{plugin-DSfS1fY2.cjs → plugin-p0c8ECxA.cjs} +1 -1
- package/dist/preview-channel-Cx0e6fye.cjs +1 -0
- package/dist/preview-channel-DW9J8t4B.js +98 -0
- package/dist/protocol-Bb_vpLKE.cjs +1 -0
- package/dist/protocol-DSqOlW2r.js +84 -0
- package/dist/types/entities/content/content.d.ts +10 -0
- package/dist/types/entities/content/content.d.ts.map +1 -1
- package/dist/types/exports.d.ts +2 -0
- package/dist/types/exports.d.ts.map +1 -1
- package/dist/types/picked.d.ts +39 -7
- package/dist/types/picked.d.ts.map +1 -1
- package/dist/types/plugins/dynamic-experiences/plugin.d.ts.map +1 -1
- package/dist/types/plugins/dynamic-experiences/types.d.ts +6 -0
- package/dist/types/plugins/dynamic-experiences/types.d.ts.map +1 -1
- package/dist/types/preview/index.d.ts +18 -0
- package/dist/types/preview/index.d.ts.map +1 -0
- package/dist/types/preview-channel.d.ts +12 -0
- package/dist/types/preview-channel.d.ts.map +1 -0
- package/dist/types/preview-receiver/apply-ops.d.ts +32 -0
- package/dist/types/preview-receiver/apply-ops.d.ts.map +1 -0
- package/dist/types/preview-receiver/index.d.ts +15 -0
- package/dist/types/preview-receiver/index.d.ts.map +1 -0
- package/dist/types/preview-receiver/protocol.d.ts +60 -0
- package/dist/types/preview-receiver/protocol.d.ts.map +1 -0
- package/dist/types/utils/content-path.d.ts +37 -0
- package/dist/types/utils/content-path.d.ts.map +1 -0
- package/dist/types/utils/inert-script-report.d.ts +40 -0
- package/dist/types/utils/inert-script-report.d.ts.map +1 -0
- package/dist/types/utils/preview-session.d.ts +12 -0
- package/dist/types/utils/preview-session.d.ts.map +1 -0
- package/package.json +14 -2
- package/dist/preview-bridge-CpX1BkSA.cjs +0 -1
- package/dist/preview-bridge-D76l-dPy.js +0 -33
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ContentPatchOp } from './apply-ops';
|
|
2
|
+
/** Current protocol version. Reported on the handshake for support and telemetry only. */
|
|
3
|
+
export declare const PREVIEW_PROTOCOL_VERSION = 1;
|
|
4
|
+
/** Receiver → embedder: the frame is listening and content is registered. */
|
|
5
|
+
export declare const PREVIEW_READY_MESSAGE = "picked-preview:ready";
|
|
6
|
+
/** Embedder → receiver: path-addressed replacements for one content record. */
|
|
7
|
+
export declare const PREVIEW_CONTENT_PATCH_MESSAGE = "picked-preview:content-patch";
|
|
8
|
+
/** Receiver → embedder: a sequence gap was detected; resend the dirty paths. */
|
|
9
|
+
export declare const PREVIEW_RESYNC_MESSAGE = "picked-preview:resync";
|
|
10
|
+
/** Receiver → embedder: cumulative count of inserted subtrees whose scripts were neutralised. */
|
|
11
|
+
export declare const PREVIEW_INERT_SCRIPTS_MESSAGE = "picked-preview:inert-scripts";
|
|
12
|
+
/** Handshake posted by the receiver to each allowlisted origin. */
|
|
13
|
+
export interface PreviewReadyMessage {
|
|
14
|
+
/** Discriminator. */
|
|
15
|
+
type: typeof PREVIEW_READY_MESSAGE;
|
|
16
|
+
/** Protocol version this receiver speaks. Diagnostics only. */
|
|
17
|
+
protocolVersion: number;
|
|
18
|
+
/** Content record ids currently registered, so the embedder knows what it can patch. */
|
|
19
|
+
contentIds: string[];
|
|
20
|
+
}
|
|
21
|
+
/** Patch posted by the embedder for one content record. Narrowed to by {@link isContentPatchMessage}. */
|
|
22
|
+
interface PreviewContentPatchMessage {
|
|
23
|
+
/** Discriminator. */
|
|
24
|
+
type: typeof PREVIEW_CONTENT_PATCH_MESSAGE;
|
|
25
|
+
/** The content record to patch. */
|
|
26
|
+
contentId: string;
|
|
27
|
+
/** Path-addressed replacements, applied in order. */
|
|
28
|
+
ops: ContentPatchOp[];
|
|
29
|
+
/** Monotonic per-record sequence number, used only to detect a dropped message. */
|
|
30
|
+
seq: number;
|
|
31
|
+
}
|
|
32
|
+
/** Resync request posted by the receiver after a sequence gap. */
|
|
33
|
+
export interface PreviewResyncMessage {
|
|
34
|
+
/** Discriminator. */
|
|
35
|
+
type: typeof PREVIEW_RESYNC_MESSAGE;
|
|
36
|
+
/** The content record whose sequence broke. */
|
|
37
|
+
contentId: string;
|
|
38
|
+
/** The last sequence number successfully applied. */
|
|
39
|
+
lastSeq: number;
|
|
40
|
+
}
|
|
41
|
+
/** Inert-script report posted by the receiver. */
|
|
42
|
+
export interface PreviewInertScriptsMessage {
|
|
43
|
+
/** Discriminator. */
|
|
44
|
+
type: typeof PREVIEW_INERT_SCRIPTS_MESSAGE;
|
|
45
|
+
/** Cumulative count of inserted subtrees whose scripts were neutralised this session. */
|
|
46
|
+
total: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Type guard for an inbound content-patch message.
|
|
50
|
+
*
|
|
51
|
+
* Validates structurally rather than trusting the sender: the message crosses a frame boundary, and
|
|
52
|
+
* an unrecognised or malformed one is ignored rather than throwing inside a customer's page.
|
|
53
|
+
*
|
|
54
|
+
* @param {unknown} data - The `MessageEvent.data` payload.
|
|
55
|
+
* @returns {boolean} True when the payload is a well-formed content-patch message.
|
|
56
|
+
* @example isContentPatchMessage(event.data)
|
|
57
|
+
*/
|
|
58
|
+
export declare function isContentPatchMessage(data: unknown): data is PreviewContentPatchMessage;
|
|
59
|
+
export {};
|
|
60
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/preview-receiver/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,0FAA0F;AAC1F,eAAO,MAAM,wBAAwB,IAAI,CAAA;AAEzC,6EAA6E;AAC7E,eAAO,MAAM,qBAAqB,yBAAyB,CAAA;AAC3D,+EAA+E;AAC/E,eAAO,MAAM,6BAA6B,iCAAiC,CAAA;AAC3E,gFAAgF;AAChF,eAAO,MAAM,sBAAsB,0BAA0B,CAAA;AAC7D,iGAAiG;AACjG,eAAO,MAAM,6BAA6B,iCAAiC,CAAA;AAE3E,mEAAmE;AACnE,MAAM,WAAW,mBAAmB;IAClC,qBAAqB;IACrB,IAAI,EAAE,OAAO,qBAAqB,CAAA;IAClC,+DAA+D;IAC/D,eAAe,EAAE,MAAM,CAAA;IACvB,wFAAwF;IACxF,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AAED,yGAAyG;AACzG,UAAU,0BAA0B;IAClC,qBAAqB;IACrB,IAAI,EAAE,OAAO,6BAA6B,CAAA;IAC1C,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,GAAG,EAAE,cAAc,EAAE,CAAA;IACrB,mFAAmF;IACnF,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,kEAAkE;AAClE,MAAM,WAAW,oBAAoB;IACnC,qBAAqB;IACrB,IAAI,EAAE,OAAO,sBAAsB,CAAA;IACnC,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,kDAAkD;AAClD,MAAM,WAAW,0BAA0B;IACzC,qBAAqB;IACrB,IAAI,EAAE,OAAO,6BAA6B,CAAA;IAC1C,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAA;CACd;AA8BD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,0BAA0B,CAavF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Content-path parsing for live-preview patches.
|
|
3
|
+
*
|
|
4
|
+
* Paths address a field inside a Content Entry's delivered `data` blob using the format the Picked
|
|
5
|
+
* API already emits for content-data validation errors — `$.sections[0].data.title`, rooted at `$`,
|
|
6
|
+
* with `.key` for object keys and `[n]` for array indices. Reusing that format keeps one convention
|
|
7
|
+
* across validation errors and preview patches instead of inventing a second.
|
|
8
|
+
*
|
|
9
|
+
* Content-model field keys are constrained to `/^[a-z][a-z0-9_]*$/` server-side, so no key can
|
|
10
|
+
* contain `.` or `[` and the string form needs no escaping.
|
|
11
|
+
*/
|
|
12
|
+
/** One step along a content path: an object key, or an array index. */
|
|
13
|
+
export type ContentPathSegment = string | number;
|
|
14
|
+
/**
|
|
15
|
+
* Parses a content path into its segments.
|
|
16
|
+
*
|
|
17
|
+
* Returns an empty list for the root path *and* for anything malformed. Callers treat an empty list
|
|
18
|
+
* as "no addressable target" and skip the operation, so a corrupt or hostile path from another
|
|
19
|
+
* frame degrades to a no-op instead of throwing inside a customer's page.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} path - The path to parse, for example `$.sections[0].data.title`.
|
|
22
|
+
* @returns {ContentPathSegment[]} The parsed segments, or `[]` for the root or an invalid path.
|
|
23
|
+
* @example
|
|
24
|
+
* parseContentPath('$.sections[0].data.title') // ['sections', 0, 'data', 'title']
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseContentPath(path: string): ContentPathSegment[];
|
|
27
|
+
/**
|
|
28
|
+
* Formats segments back into a content path. Inverse of {@link parseContentPath} for every
|
|
29
|
+
* well-formed path.
|
|
30
|
+
*
|
|
31
|
+
* @param {readonly ContentPathSegment[]} segments - The segments to format.
|
|
32
|
+
* @returns {string} The path string, or `'$'` when there are no segments.
|
|
33
|
+
* @example
|
|
34
|
+
* formatContentPath(['sections', 0, 'data', 'title']) // '$.sections[0].data.title'
|
|
35
|
+
*/
|
|
36
|
+
export declare function formatContentPath(segments: readonly ContentPathSegment[]): string;
|
|
37
|
+
//# sourceMappingURL=content-path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-path.d.ts","sourceRoot":"","sources":["../../../src/utils/content-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,uEAAuE;AACvE,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,MAAM,CAAA;AA2ChD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAYnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,GAAG,MAAM,CAOjF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Relays the count of preview-inserted subtrees whose scripts were neutralised.
|
|
3
|
+
*
|
|
4
|
+
* The render layer (`@picked/react`'s `HtmlRender`) knows how many script-bearing subtrees a
|
|
5
|
+
* preview morph inserted; the preview receiver knows how to tell the dashboard. Neither should
|
|
6
|
+
* import the other, so the count passes through this tiny module. Every framework binding's render
|
|
7
|
+
* path reports into the same receiver through it.
|
|
8
|
+
*
|
|
9
|
+
* The count is cumulative for the session and only ever grows, which matches how the editor uses
|
|
10
|
+
* it: "some markup you added is inert until you save", not a per-edit delta.
|
|
11
|
+
*/
|
|
12
|
+
/** Listener notified when the cumulative inert-subtree count changes. */
|
|
13
|
+
type InertScriptListener = (total: number) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Records that a preview render inserted `count` subtrees whose scripts were neutralised.
|
|
16
|
+
*
|
|
17
|
+
* @param {number} count - Subtrees neutralised by the render that just completed.
|
|
18
|
+
* @returns {void} Nothing.
|
|
19
|
+
* @example reportInertScriptSubtrees(2)
|
|
20
|
+
*/
|
|
21
|
+
export declare function reportInertScriptSubtrees(count: number): void;
|
|
22
|
+
/**
|
|
23
|
+
* Subscribes to the cumulative inert-subtree count, invoking `listener` immediately with the
|
|
24
|
+
* current total so a late subscriber (the receiver connects after first paint) is not left behind.
|
|
25
|
+
*
|
|
26
|
+
* @param {InertScriptListener} listener - Called with the running total.
|
|
27
|
+
* @returns {() => void} Unsubscribe function.
|
|
28
|
+
* @example const stop = onInertScriptSubtrees((n) => post({ inertScriptSubtrees: n }))
|
|
29
|
+
*/
|
|
30
|
+
export declare function onInertScriptSubtrees(listener: InertScriptListener): () => void;
|
|
31
|
+
/**
|
|
32
|
+
* Resets the cumulative count and drops every listener. Exposed for tests, which share a module
|
|
33
|
+
* registry across cases.
|
|
34
|
+
*
|
|
35
|
+
* @returns {void} Nothing.
|
|
36
|
+
* @example resetInertScriptReport()
|
|
37
|
+
*/
|
|
38
|
+
export declare function resetInertScriptReport(): void;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=inert-script-report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inert-script-report.d.ts","sourceRoot":"","sources":["../../../src/utils/inert-script-report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,yEAAyE;AACzE,KAAK,mBAAmB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;AAKlD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAI7D;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,IAAI,CAM/E;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Search parameter the dashboard appends to the preview iframe's URL. */
|
|
2
|
+
export declare const PREVIEW_TOKEN_PARAM = "preview-token";
|
|
3
|
+
/**
|
|
4
|
+
* Whether this frame is running as a Picked live-preview session: inside an iframe, with a preview
|
|
5
|
+
* token on the URL.
|
|
6
|
+
*
|
|
7
|
+
* @returns {boolean} True when the frame is a preview session.
|
|
8
|
+
* @example
|
|
9
|
+
* if (isPreviewSession()) enableMorphingRender()
|
|
10
|
+
*/
|
|
11
|
+
export declare function isPreviewSession(): boolean;
|
|
12
|
+
//# sourceMappingURL=preview-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-session.d.ts","sourceRoot":"","sources":["../../../src/utils/preview-session.ts"],"names":[],"mappings":"AAUA,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,kBAAkB,CAAA;AAElD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAG1C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@picked/client",
|
|
3
|
-
"version": "0.0.70-next.
|
|
3
|
+
"version": "0.0.70-next.2",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@withease/factories": "^1.0.5"
|
|
6
6
|
},
|
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
"require": "./dist/cjs/server/index.js",
|
|
36
36
|
"default": "./dist/esm/server/index.js"
|
|
37
37
|
},
|
|
38
|
+
"./preview": {
|
|
39
|
+
"types": "./dist/types/preview/index.d.ts",
|
|
40
|
+
"import": "./dist/esm/preview/index.js",
|
|
41
|
+
"require": "./dist/cjs/preview/index.js",
|
|
42
|
+
"default": "./dist/esm/preview/index.js"
|
|
43
|
+
},
|
|
38
44
|
"./plugins/single-page-app": {
|
|
39
45
|
"types": "./dist/types/plugins/single-page-app/index.d.ts",
|
|
40
46
|
"import": "./dist/esm/plugins/single-page-app/index.js",
|
|
@@ -166,12 +172,18 @@
|
|
|
166
172
|
"size-limit": [
|
|
167
173
|
{
|
|
168
174
|
"path": "dist/esm/index.js",
|
|
169
|
-
"limit": "
|
|
175
|
+
"limit": "16 kB",
|
|
176
|
+
"name": "index (incl. lazily-loaded preview channel; see AGENTS.md)"
|
|
170
177
|
},
|
|
171
178
|
{
|
|
172
179
|
"path": "dist/esm/server/index.js",
|
|
173
180
|
"limit": "10 kB"
|
|
174
181
|
},
|
|
182
|
+
{
|
|
183
|
+
"path": "dist/esm/preview/index.js",
|
|
184
|
+
"limit": "5 kB",
|
|
185
|
+
"name": "preview helpers (dashboard-only subpath)"
|
|
186
|
+
},
|
|
175
187
|
{
|
|
176
188
|
"path": "dist/esm/plugins/single-page-app/index.js",
|
|
177
189
|
"limit": "5 kB"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e=require("./object-CIkt7Fkr.cjs"),t=require("./supports-DBzjGaWD.cjs");var n=`picked-preview:navigation`,r=!1;function i(){return`*`}function a(){let{href:e,pathname:t,search:r}=globalThis.location,a={type:n,href:e,pathname:t,search:r};globalThis.parent.postMessage(a,i())}function o(e){return function(...t){e.apply(this,t),a()}}function s(){if(r||!t.f())return()=>void 0;r=!0,a();let n=()=>{a()};return globalThis.addEventListener(`popstate`,n),t.a()&&(e.n(globalThis.history,`pushState`,o),e.n(globalThis.history,`replaceState`,o)),()=>{r=!1,globalThis.removeEventListener(`popstate`,n)}}exports.initPreviewBridge=s;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { n as e } from "./object-BUDGxEqy.js";
|
|
2
|
-
import { a as t, f as n } from "./supports-CO9bJGNl.js";
|
|
3
|
-
//#region src/preview-bridge/index.ts
|
|
4
|
-
var r = "picked-preview:navigation", i = !1;
|
|
5
|
-
function a() {
|
|
6
|
-
return "*";
|
|
7
|
-
}
|
|
8
|
-
function o() {
|
|
9
|
-
let { href: e, pathname: t, search: n } = globalThis.location, i = {
|
|
10
|
-
type: r,
|
|
11
|
-
href: e,
|
|
12
|
-
pathname: t,
|
|
13
|
-
search: n
|
|
14
|
-
};
|
|
15
|
-
globalThis.parent.postMessage(i, a());
|
|
16
|
-
}
|
|
17
|
-
function s(e) {
|
|
18
|
-
return function(...t) {
|
|
19
|
-
e.apply(this, t), o();
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
function c() {
|
|
23
|
-
if (i || !n()) return () => void 0;
|
|
24
|
-
i = !0, o();
|
|
25
|
-
let r = () => {
|
|
26
|
-
o();
|
|
27
|
-
};
|
|
28
|
-
return globalThis.addEventListener("popstate", r), t() && (e(globalThis.history, "pushState", s), e(globalThis.history, "replaceState", s)), () => {
|
|
29
|
-
i = !1, globalThis.removeEventListener("popstate", r);
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
//#endregion
|
|
33
|
-
export { c as initPreviewBridge };
|