@preprio/toolkit 0.4.3 → 0.5.0-rc.10

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.
Files changed (61) hide show
  1. package/README.md +416 -23
  2. package/dist/astro/index.cjs +1290 -1
  3. package/dist/astro/index.d.cts +47 -1
  4. package/dist/astro/index.d.ts +47 -1
  5. package/dist/astro/index.js +1297 -1
  6. package/dist/chunk-F46GJS6Y.js +26 -0
  7. package/dist/{chunk-H7VA7NGY.js → chunk-IDEC77LZ.js} +1480 -621
  8. package/dist/chunk-IPMB32AZ.js +246 -0
  9. package/dist/chunk-KD3GK6TO.js +1221 -0
  10. package/dist/chunk-S3VT4UWM.js +75 -0
  11. package/dist/chunk-TBBU3LDD.js +184 -0
  12. package/dist/chunk-URXSDJOU.js +356 -0
  13. package/dist/codegen/index.cjs +82 -0
  14. package/dist/codegen/index.d.cts +70 -0
  15. package/dist/codegen/index.d.ts +70 -0
  16. package/dist/codegen/index.js +64 -0
  17. package/dist/index.cjs +3412 -409
  18. package/dist/index.d.cts +287 -5
  19. package/dist/index.d.ts +287 -5
  20. package/dist/index.js +307 -144
  21. package/dist/live-preview-DLXGXRE5.js +18 -0
  22. package/dist/nextjs/components.cjs +1662 -388
  23. package/dist/nextjs/components.d.cts +1 -1
  24. package/dist/nextjs/components.d.ts +1 -1
  25. package/dist/nextjs/components.js +4 -1
  26. package/dist/nextjs/index.cjs +1181 -6
  27. package/dist/nextjs/index.d.cts +83 -3
  28. package/dist/nextjs/index.d.ts +83 -3
  29. package/dist/nextjs/index.js +1179 -7
  30. package/dist/nextjs/live-action.cjs +285 -0
  31. package/dist/nextjs/live-action.d.cts +12 -0
  32. package/dist/nextjs/live-action.d.ts +12 -0
  33. package/dist/nextjs/live-action.js +68 -0
  34. package/dist/nextjs/live.cjs +1899 -0
  35. package/dist/nextjs/live.d.cts +54 -0
  36. package/dist/nextjs/live.d.ts +54 -0
  37. package/dist/nextjs/live.js +268 -0
  38. package/dist/nuxt/chunk-BWPGO25E.js +340 -0
  39. package/dist/nuxt/index.cjs +1 -1
  40. package/dist/nuxt/index.js +1 -1
  41. package/dist/nuxt/live-preview-2RCU35W5.js +1189 -0
  42. package/dist/nuxt/live.cjs +1816 -0
  43. package/dist/nuxt/live.d.cts +43 -0
  44. package/dist/nuxt/live.d.ts +43 -0
  45. package/dist/nuxt/live.js +198 -0
  46. package/dist/{pixel-e1fUWl2Y.d.cts → pixel-CX422dCO.d.cts} +18 -3
  47. package/dist/{pixel-e1fUWl2Y.d.ts → pixel-CX422dCO.d.ts} +18 -3
  48. package/dist/react/components.cjs +1662 -388
  49. package/dist/react/components.d.cts +17 -2
  50. package/dist/react/components.d.ts +17 -2
  51. package/dist/react/components.js +1662 -388
  52. package/dist/sveltekit/chunk-BWPGO25E.js +340 -0
  53. package/dist/sveltekit/index.cjs +1 -1
  54. package/dist/sveltekit/index.js +1 -1
  55. package/dist/sveltekit/live-preview-2RCU35W5.js +1189 -0
  56. package/dist/sveltekit/live.cjs +1830 -0
  57. package/dist/sveltekit/live.d.cts +59 -0
  58. package/dist/sveltekit/live.d.ts +59 -0
  59. package/dist/sveltekit/live.js +204 -0
  60. package/package.json +37 -11
  61. package/src/astro/PreprToolbar.astro +7 -1
@@ -0,0 +1,43 @@
1
+ import { Ref, ComputedRef } from 'vue';
2
+
3
+ /**
4
+ * What `normalizeQuery` accepts as the query: GraphQL source text, a parsed
5
+ * `DocumentNode` (from `graphql-tag`, Apollo or GraphQL Code Generator), or a
6
+ * string-like object such as codegen's `TypedDocumentString`.
7
+ */
8
+ type QueryInput = string | {
9
+ kind: 'Document';
10
+ } | {
11
+ toString(): string;
12
+ };
13
+
14
+ interface UseNormalizedQueryOptions {
15
+ /**
16
+ * Pass your preview flag; never `true` on a production deployment.
17
+ * Defaults to false: the data is returned unchanged.
18
+ */
19
+ enabled?: boolean;
20
+ /** Called when the editor saves, e.g. `refresh` from `useAsyncData`. */
21
+ onSave?: () => void;
22
+ /** Exact editor origins, replacing the `*.prepr.io` default. */
23
+ allowedEditorOrigins?: string[];
24
+ /** See the toolbar's `strictMode`. */
25
+ strictMode?: boolean;
26
+ }
27
+ /**
28
+ * Show the Prepr editor's unsaved changes in a Nuxt page, applied in the
29
+ * browser while the editor types:
30
+ *
31
+ * ```ts
32
+ * const page = useNormalizedQuery(GetPageDocument, data, {
33
+ * enabled: isPreview,
34
+ * onSave: refresh,
35
+ * });
36
+ * ```
37
+ *
38
+ * Returns the data unchanged on the server and outside the editor iframe.
39
+ * Select `_id` on every item and component that should update.
40
+ */
41
+ declare function useNormalizedQuery<T>(query: QueryInput, data: Ref<T> | T, options?: UseNormalizedQueryOptions): ComputedRef<T>;
42
+
43
+ export { type UseNormalizedQueryOptions, useNormalizedQuery };
@@ -0,0 +1,43 @@
1
+ import { Ref, ComputedRef } from 'vue';
2
+
3
+ /**
4
+ * What `normalizeQuery` accepts as the query: GraphQL source text, a parsed
5
+ * `DocumentNode` (from `graphql-tag`, Apollo or GraphQL Code Generator), or a
6
+ * string-like object such as codegen's `TypedDocumentString`.
7
+ */
8
+ type QueryInput = string | {
9
+ kind: 'Document';
10
+ } | {
11
+ toString(): string;
12
+ };
13
+
14
+ interface UseNormalizedQueryOptions {
15
+ /**
16
+ * Pass your preview flag; never `true` on a production deployment.
17
+ * Defaults to false: the data is returned unchanged.
18
+ */
19
+ enabled?: boolean;
20
+ /** Called when the editor saves, e.g. `refresh` from `useAsyncData`. */
21
+ onSave?: () => void;
22
+ /** Exact editor origins, replacing the `*.prepr.io` default. */
23
+ allowedEditorOrigins?: string[];
24
+ /** See the toolbar's `strictMode`. */
25
+ strictMode?: boolean;
26
+ }
27
+ /**
28
+ * Show the Prepr editor's unsaved changes in a Nuxt page, applied in the
29
+ * browser while the editor types:
30
+ *
31
+ * ```ts
32
+ * const page = useNormalizedQuery(GetPageDocument, data, {
33
+ * enabled: isPreview,
34
+ * onSave: refresh,
35
+ * });
36
+ * ```
37
+ *
38
+ * Returns the data unchanged on the server and outside the editor iframe.
39
+ * Select `_id` on every item and component that should update.
40
+ */
41
+ declare function useNormalizedQuery<T>(query: QueryInput, data: Ref<T> | T, options?: UseNormalizedQueryOptions): ComputedRef<T>;
42
+
43
+ export { type UseNormalizedQueryOptions, useNormalizedQuery };
@@ -0,0 +1,198 @@
1
+ import {
2
+ collectFields,
3
+ readQueryDocument
4
+ } from "./chunk-BWPGO25E.js";
5
+
6
+ // src/nuxt/live.ts
7
+ import {
8
+ computed,
9
+ getCurrentScope,
10
+ onScopeDispose,
11
+ shallowRef,
12
+ unref
13
+ } from "vue";
14
+
15
+ // src/core/live-preview/edit-links.ts
16
+ import {
17
+ vercelStegaCombine,
18
+ vercelStegaDecode,
19
+ vercelStegaSplit
20
+ } from "@vercel/stega";
21
+ var ENTRY_PARAM = "entry";
22
+ var TOP_LEVEL_ENTRY = /^[A-Za-z0-9_]+\.\d+$/;
23
+ function entryIdOf(element) {
24
+ if (!isRecord(element)) return void 0;
25
+ if (typeof element._id === "string") return element._id;
26
+ if (element.__typename !== "Assets" || !Array.isArray(element.items)) {
27
+ return void 0;
28
+ }
29
+ const first = element.items[0];
30
+ return isRecord(first) && typeof first._id === "string" ? first._id : void 0;
31
+ }
32
+ function isRecord(value) {
33
+ return typeof value === "object" && value !== null && !Array.isArray(value);
34
+ }
35
+ function annotate(value, ctx) {
36
+ if (!ctx.entryId) return value;
37
+ const { cleaned, encoded } = vercelStegaSplit(value);
38
+ if (!encoded) return value;
39
+ let payload;
40
+ try {
41
+ payload = vercelStegaDecode(encoded);
42
+ } catch {
43
+ return value;
44
+ }
45
+ if (typeof payload?.href !== "string") return value;
46
+ let url;
47
+ try {
48
+ url = new URL(payload.href);
49
+ } catch {
50
+ return value;
51
+ }
52
+ if (url.searchParams.has(ENTRY_PARAM)) return value;
53
+ url.searchParams.set(ENTRY_PARAM, ctx.entryId);
54
+ const itemId = decodeURIComponent(url.pathname.split("/").pop() ?? "");
55
+ if (!url.searchParams.has("field") && ctx.entryField && TOP_LEVEL_ENTRY.test(ctx.entryField) && itemId === ctx.entryOwnerId) {
56
+ url.searchParams.set("field", ctx.entryField);
57
+ }
58
+ return vercelStegaCombine(
59
+ cleaned,
60
+ { ...payload, href: url.toString() },
61
+ false
62
+ );
63
+ }
64
+ function walk(value, selections, ctx) {
65
+ if (typeof value === "string") return annotate(value, ctx);
66
+ if (Array.isArray(value)) {
67
+ let changed2 = false;
68
+ const next2 = value.map((element, index) => {
69
+ const path = [...ctx.path, String(index)];
70
+ const entryId = ctx.inAssets ? void 0 : entryIdOf(element);
71
+ const entry = entryId ? {
72
+ entryId,
73
+ entryField: ctx.ownerId ? path.join(".") : void 0,
74
+ entryOwnerId: ctx.ownerId,
75
+ inAssets: isRecord(element) && element._id === void 0
76
+ } : {};
77
+ const out = walk(element, selections, { ...ctx, ...entry, path });
78
+ if (out !== element) changed2 = true;
79
+ return out;
80
+ });
81
+ return changed2 ? next2 : value;
82
+ }
83
+ if (!isRecord(value)) return value;
84
+ const id = typeof value._id === "string" ? value._id : void 0;
85
+ const typename = typeof value.__typename === "string" ? value.__typename : void 0;
86
+ const fields = ctx.doc && selections ? collectFields(selections, typename, ctx.doc) : null;
87
+ let changed = false;
88
+ const next = {};
89
+ for (const [key, child] of Object.entries(value)) {
90
+ const field = fields?.get(key);
91
+ const name = field?.name ?? key;
92
+ const out = walk(child, field?.selections ?? null, {
93
+ ...ctx,
94
+ ownerId: id ?? ctx.ownerId,
95
+ path: id ? [name] : [...ctx.path, name]
96
+ });
97
+ if (out !== child) changed = true;
98
+ next[key] = out;
99
+ }
100
+ return changed ? next : value;
101
+ }
102
+ function annotateEditLinks(result, query, operationName) {
103
+ let doc = null;
104
+ if (query !== void 0) {
105
+ try {
106
+ doc = readQueryDocument(query, operationName);
107
+ } catch {
108
+ doc = null;
109
+ }
110
+ }
111
+ return walk(result, doc?.operation ?? null, { doc, path: [] });
112
+ }
113
+
114
+ // src/core/live-preview/test-reset.ts
115
+ var resets = /* @__PURE__ */ new Set();
116
+ function onTestReset(reset) {
117
+ resets.add(reset);
118
+ }
119
+
120
+ // src/nuxt/live.ts
121
+ var starting = null;
122
+ var store = null;
123
+ var bridge = null;
124
+ var applyLog = null;
125
+ var log = shallowRef([]);
126
+ var allowedEditorOrigins;
127
+ var strictMode;
128
+ var startListeners = /* @__PURE__ */ new Set();
129
+ function isFramed() {
130
+ return typeof window !== "undefined" && window.self !== window.top;
131
+ }
132
+ function startLive() {
133
+ starting ?? (starting = import("./live-preview-2RCU35W5.js").then(
134
+ ({ applyPatchLog, createLiveBridge, createLiveStore }) => {
135
+ applyLog = applyPatchLog;
136
+ const started = createLiveStore();
137
+ bridge = createLiveBridge(started, {
138
+ allowedEditorOrigins: () => allowedEditorOrigins,
139
+ strictMode: () => strictMode
140
+ });
141
+ bridge.start();
142
+ started.onLogChange((next) => {
143
+ log.value = next;
144
+ });
145
+ store = started;
146
+ startListeners.forEach((listener) => listener(started));
147
+ startListeners.clear();
148
+ }
149
+ ));
150
+ }
151
+ function whenLive(listener) {
152
+ if (store) {
153
+ listener(store);
154
+ return () => {
155
+ };
156
+ }
157
+ startListeners.add(listener);
158
+ return () => startListeners.delete(listener);
159
+ }
160
+ function useNormalizedQuery(query, data, options = {}) {
161
+ if (!options.enabled) return computed(() => unref(data));
162
+ const annotated = computed(() => annotateEditLinks(unref(data), query));
163
+ if (!isFramed()) return annotated;
164
+ if (options.strictMode) strictMode = true;
165
+ if (options.allowedEditorOrigins) {
166
+ allowedEditorOrigins = [...options.allowedEditorOrigins];
167
+ }
168
+ startLive();
169
+ if (options.onSave && getCurrentScope()) {
170
+ const onSave = options.onSave;
171
+ let offInvalidate = null;
172
+ const stopWaiting = whenLive((started) => {
173
+ offInvalidate = started.onInvalidate(() => onSave());
174
+ });
175
+ onScopeDispose(() => {
176
+ stopWaiting();
177
+ offInvalidate?.();
178
+ });
179
+ }
180
+ return computed(() => {
181
+ const value = annotated.value;
182
+ return log.value.length && applyLog ? applyLog(query, value, log.value) : value;
183
+ });
184
+ }
185
+ onTestReset(() => {
186
+ bridge?.stop();
187
+ bridge = null;
188
+ store = null;
189
+ starting = null;
190
+ applyLog = null;
191
+ startListeners.clear();
192
+ log.value = [];
193
+ allowedEditorOrigins = void 0;
194
+ strictMode = void 0;
195
+ });
196
+ export {
197
+ useNormalizedQuery
198
+ };
@@ -2,6 +2,8 @@ type Dictionary = Record<string, unknown>;
2
2
  declare const dictionaries: {
3
3
  en: Dictionary;
4
4
  nl: Dictionary;
5
+ fr: Dictionary;
6
+ de: Dictionary;
5
7
  };
6
8
  type Locale = keyof typeof dictionaries;
7
9
 
@@ -64,10 +66,23 @@ interface PreprPreviewOptions extends PreprToolbarOptions {
64
66
  */
65
67
  ui?: boolean;
66
68
  /**
67
- * Replace the `*.prepr.io` editor-origin wildcard with an exact list.
68
- * Intended for self-hosted editors; when set, the wildcard no longer applies.
69
+ * Replace the `*.prepr.io` editor-origin wildcard with an exact list, e.g.
70
+ * `['http://acme.localhost:8000']`. Intended for self-hosted or locally run
71
+ * editors; when set, the wildcard no longer applies. Entries are compared to
72
+ * the editor's origin exactly: scheme, host and port.
73
+ *
74
+ * `['*']` accepts any editor origin. Use it for development only: any page
75
+ * that frames the preview can then switch on preview and edit mode and
76
+ * receives the field-edit events, which carry content item ids.
69
77
  */
70
78
  allowedEditorOrigins?: string[];
79
+ /**
80
+ * Set when your preview token uses GraphQL strict mode, where required
81
+ * fields are never null. The editor then holds back new entries until
82
+ * their required fields are filled, and never sends a cleared required
83
+ * field as null.
84
+ */
85
+ strictMode?: boolean;
71
86
  /**
72
87
  * Strip the invisible stega characters out of visible text at runtime.
73
88
  * Default `true`.
@@ -111,7 +126,7 @@ interface PreprToolbarComponentProps extends PreprToolbarProps {
111
126
  type PreprHeaderName = 'prepr-customer-id' | 'Prepr-Segments' | 'Prepr-ABtesting' | 'User-Agent' | 'Prepr-User-Agent';
112
127
  type PreprVariant = 'A' | 'B';
113
128
  type PreprEnvironment = 'preview' | 'production';
114
- type PreprEventType = 'segment_changed' | 'variant_changed' | 'edit_mode_toggled' | 'preview_mode_toggled' | 'getScrollPosition' | 'loaded' | 'field_edit_requested';
129
+ type PreprEventType = 'segment_changed' | 'variant_changed' | 'edit_mode_toggled' | 'preview_mode_toggled' | 'getScrollPosition' | 'loaded' | 'field_edit_requested' | 'live_ready' | 'navigation_blocked';
115
130
  interface PreprHeaders {
116
131
  readonly 'prepr-customer-id'?: string;
117
132
  readonly 'Prepr-Segments'?: string;
@@ -2,6 +2,8 @@ type Dictionary = Record<string, unknown>;
2
2
  declare const dictionaries: {
3
3
  en: Dictionary;
4
4
  nl: Dictionary;
5
+ fr: Dictionary;
6
+ de: Dictionary;
5
7
  };
6
8
  type Locale = keyof typeof dictionaries;
7
9
 
@@ -64,10 +66,23 @@ interface PreprPreviewOptions extends PreprToolbarOptions {
64
66
  */
65
67
  ui?: boolean;
66
68
  /**
67
- * Replace the `*.prepr.io` editor-origin wildcard with an exact list.
68
- * Intended for self-hosted editors; when set, the wildcard no longer applies.
69
+ * Replace the `*.prepr.io` editor-origin wildcard with an exact list, e.g.
70
+ * `['http://acme.localhost:8000']`. Intended for self-hosted or locally run
71
+ * editors; when set, the wildcard no longer applies. Entries are compared to
72
+ * the editor's origin exactly: scheme, host and port.
73
+ *
74
+ * `['*']` accepts any editor origin. Use it for development only: any page
75
+ * that frames the preview can then switch on preview and edit mode and
76
+ * receives the field-edit events, which carry content item ids.
69
77
  */
70
78
  allowedEditorOrigins?: string[];
79
+ /**
80
+ * Set when your preview token uses GraphQL strict mode, where required
81
+ * fields are never null. The editor then holds back new entries until
82
+ * their required fields are filled, and never sends a cleared required
83
+ * field as null.
84
+ */
85
+ strictMode?: boolean;
71
86
  /**
72
87
  * Strip the invisible stega characters out of visible text at runtime.
73
88
  * Default `true`.
@@ -111,7 +126,7 @@ interface PreprToolbarComponentProps extends PreprToolbarProps {
111
126
  type PreprHeaderName = 'prepr-customer-id' | 'Prepr-Segments' | 'Prepr-ABtesting' | 'User-Agent' | 'Prepr-User-Agent';
112
127
  type PreprVariant = 'A' | 'B';
113
128
  type PreprEnvironment = 'preview' | 'production';
114
- type PreprEventType = 'segment_changed' | 'variant_changed' | 'edit_mode_toggled' | 'preview_mode_toggled' | 'getScrollPosition' | 'loaded' | 'field_edit_requested';
129
+ type PreprEventType = 'segment_changed' | 'variant_changed' | 'edit_mode_toggled' | 'preview_mode_toggled' | 'getScrollPosition' | 'loaded' | 'field_edit_requested' | 'live_ready' | 'navigation_blocked';
115
130
  interface PreprHeaders {
116
131
  readonly 'prepr-customer-id'?: string;
117
132
  readonly 'Prepr-Segments'?: string;