@payload-onpage/core 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +18 -2
- package/dist/autosave.d.ts +53 -0
- package/dist/autosave.d.ts.map +1 -0
- package/dist/autosave.js +129 -0
- package/dist/autosave.js.map +1 -0
- package/dist/constants.d.ts +24 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +24 -0
- package/dist/constants.js.map +1 -0
- package/dist/docref.d.ts +19 -0
- package/dist/docref.d.ts.map +1 -0
- package/dist/docref.js +23 -0
- package/dist/docref.js.map +1 -0
- package/dist/encode.d.ts +17 -0
- package/dist/encode.d.ts.map +1 -0
- package/dist/encode.js +109 -0
- package/dist/encode.js.map +1 -0
- package/dist/fields.d.ts +72 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +267 -0
- package/dist/fields.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/lexical.d.ts +21 -0
- package/dist/lexical.d.ts.map +1 -0
- package/dist/lexical.js +32 -0
- package/dist/lexical.js.map +1 -0
- package/dist/path.d.ts +35 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +124 -0
- package/dist/path.js.map +1 -0
- package/dist/payload.d.ts +19 -0
- package/dist/payload.d.ts.map +1 -0
- package/dist/payload.js +162 -0
- package/dist/payload.js.map +1 -0
- package/dist/ports.d.ts +151 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +2 -0
- package/dist/ports.js.map +1 -0
- package/dist/schema.d.ts +71 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +7 -0
- package/dist/schema.js.map +1 -0
- package/dist/seal.d.ts +12 -0
- package/dist/seal.d.ts.map +1 -0
- package/dist/seal.js +84 -0
- package/dist/seal.js.map +1 -0
- package/dist/session.d.ts +120 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +391 -0
- package/dist/session.js.map +1 -0
- package/dist/stega.d.ts +35 -0
- package/dist/stega.d.ts.map +1 -0
- package/dist/stega.js +111 -0
- package/dist/stega.js.map +1 -0
- package/package.json +56 -4
- package/src/autosave.ts +156 -0
- package/src/constants.ts +25 -0
- package/src/docref.ts +32 -0
- package/src/encode.ts +133 -0
- package/src/fields.ts +300 -0
- package/src/index.ts +18 -0
- package/src/lexical.ts +46 -0
- package/src/path.ts +133 -0
- package/src/payload.ts +218 -0
- package/src/ports.ts +144 -0
- package/src/schema.ts +62 -0
- package/src/seal.ts +102 -0
- package/src/session.ts +438 -0
- package/src/stega.ts +120 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jellologic
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @payload-onpage/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Framework-free building blocks: document refs, paths and field operations, the editor session (multi-document, undo, serialized publish/discard), autosave with stale-write detection, invisible field markers, sealing, and the **port interfaces** adapters implement.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { EditorSession } from '@payload-onpage/core'
|
|
7
|
+
import { payloadRest } from '@payload-onpage/core/payload'
|
|
8
|
+
|
|
9
|
+
const session = new EditorSession({ dataSource: payloadRest() })
|
|
10
|
+
await session.open({ kind: 'collection', slug: 'pages', id: 1 })
|
|
11
|
+
session.setValue('c:pages:1', 'title', 'Hello')
|
|
12
|
+
await session.publish()
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Entries: `@payload-onpage/core` (logic, ports), `@payload-onpage/core/payload` (Payload REST `DataSource` + `MediaLibrary`).
|
|
16
|
+
|
|
17
|
+
Part of [**payload-onpage**](https://github.com/jellologic/payload-onpage): on-page editing and draft previews for Payload CMS + Next.js. Start with the [getting started guide](https://github.com/jellologic/payload-onpage/blob/main/docs/getting-started.md).
|
|
18
|
+
|
|
19
|
+
MIT licensed.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debounced, single-flight autosave. Patches scheduled while a save is in
|
|
3
|
+
* flight are merged and sent once it settles, so the server always ends with
|
|
4
|
+
* the latest value of every field. A conflict (409) freezes the queue until
|
|
5
|
+
* `reset()`. Transient failures (network, 429, 5xx) retry with backoff; other
|
|
6
|
+
* failures (validation, permissions) stop immediately with the server's message.
|
|
7
|
+
*/
|
|
8
|
+
import type { SaveResult } from './ports.ts';
|
|
9
|
+
export type SaveStatus = 'idle' | 'pending' | 'saving' | 'saved' | 'error' | 'conflict';
|
|
10
|
+
export interface SaveState {
|
|
11
|
+
status: SaveStatus;
|
|
12
|
+
lastSavedAt?: number;
|
|
13
|
+
message?: string;
|
|
14
|
+
/** Increments with every successful save. */
|
|
15
|
+
seq: number;
|
|
16
|
+
}
|
|
17
|
+
export interface AutosaveOptions {
|
|
18
|
+
send: (patch: Record<string, unknown>) => Promise<SaveResult>;
|
|
19
|
+
delay?: number;
|
|
20
|
+
retries?: number;
|
|
21
|
+
onState?: (state: SaveState) => void;
|
|
22
|
+
/** Injected for tests. */
|
|
23
|
+
setTimer?: (fn: () => void, ms: number) => unknown;
|
|
24
|
+
clearTimer?: (handle: unknown) => void;
|
|
25
|
+
}
|
|
26
|
+
export declare class AutosaveQueue {
|
|
27
|
+
private readonly opts;
|
|
28
|
+
private pending;
|
|
29
|
+
private timer;
|
|
30
|
+
private inFlight;
|
|
31
|
+
private attempt;
|
|
32
|
+
private state;
|
|
33
|
+
private readonly delay;
|
|
34
|
+
private readonly retries;
|
|
35
|
+
private readonly setTimer;
|
|
36
|
+
private readonly clearTimer;
|
|
37
|
+
constructor(opts: AutosaveOptions);
|
|
38
|
+
get current(): SaveState;
|
|
39
|
+
get hasPending(): boolean;
|
|
40
|
+
private setState;
|
|
41
|
+
/** Queue fields to save; later values for the same key replace earlier ones. */
|
|
42
|
+
schedule(patch: Record<string, unknown>): void;
|
|
43
|
+
private arm;
|
|
44
|
+
/** Send now (also used before publishing). Resolves when idle. */
|
|
45
|
+
flush(): Promise<void>;
|
|
46
|
+
/** Clear a conflict or error (after the caller reloaded the document). Drops unsent changes. */
|
|
47
|
+
reset(): void;
|
|
48
|
+
/** Retry after an error, keeping the unsent changes. */
|
|
49
|
+
retry(): void;
|
|
50
|
+
/** Stop timers (unmount). Unsent changes are dropped; call flush() first to keep them. */
|
|
51
|
+
dispose(): void;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=autosave.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autosave.d.ts","sourceRoot":"","sources":["../src/autosave.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,CAAA;AAEvF,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,UAAU,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAA;IACpC,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAA;IAClD,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC;AAID,qBAAa,aAAa;IAWZ,OAAO,CAAC,QAAQ,CAAC,IAAI;IAVjC,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,OAAO,CAAI;IACnB,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyC;IAClE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2B;gBAEzB,IAAI,EAAE,eAAe;IAOlD,IAAI,OAAO,IAAI,SAAS,CAEvB;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,OAAO,CAAC,QAAQ;IAKhB,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAOvC,OAAO,CAAC,GAAG;IAQX,kEAAkE;IAC5D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuD5B,gGAAgG;IAChG,KAAK;IAQL,wDAAwD;IACxD,KAAK;IAML,0FAA0F;IAC1F,OAAO;CAIR"}
|
package/dist/autosave.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const isTransient = (status) => !status || status === 429 || status >= 500;
|
|
2
|
+
export class AutosaveQueue {
|
|
3
|
+
opts;
|
|
4
|
+
pending = null;
|
|
5
|
+
timer = null;
|
|
6
|
+
inFlight = null;
|
|
7
|
+
attempt = 0;
|
|
8
|
+
state = { status: 'idle', seq: 0 };
|
|
9
|
+
delay;
|
|
10
|
+
retries;
|
|
11
|
+
setTimer;
|
|
12
|
+
clearTimer;
|
|
13
|
+
constructor(opts) {
|
|
14
|
+
this.opts = opts;
|
|
15
|
+
this.delay = opts.delay ?? 800;
|
|
16
|
+
this.retries = opts.retries ?? 3;
|
|
17
|
+
this.setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
18
|
+
this.clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
|
|
19
|
+
}
|
|
20
|
+
get current() {
|
|
21
|
+
return this.state;
|
|
22
|
+
}
|
|
23
|
+
get hasPending() {
|
|
24
|
+
return this.pending !== null || this.inFlight !== null;
|
|
25
|
+
}
|
|
26
|
+
setState(next) {
|
|
27
|
+
this.state = { ...this.state, ...next };
|
|
28
|
+
this.opts.onState?.(this.state);
|
|
29
|
+
}
|
|
30
|
+
/** Queue fields to save; later values for the same key replace earlier ones. */
|
|
31
|
+
schedule(patch) {
|
|
32
|
+
if (this.state.status === 'conflict')
|
|
33
|
+
return;
|
|
34
|
+
this.pending = { ...(this.pending ?? {}), ...patch };
|
|
35
|
+
this.setState({ status: this.inFlight ? 'saving' : 'pending' });
|
|
36
|
+
this.arm(this.delay);
|
|
37
|
+
}
|
|
38
|
+
arm(ms) {
|
|
39
|
+
if (this.timer)
|
|
40
|
+
this.clearTimer(this.timer);
|
|
41
|
+
this.timer = this.setTimer(() => {
|
|
42
|
+
this.timer = null;
|
|
43
|
+
void this.flush();
|
|
44
|
+
}, ms);
|
|
45
|
+
}
|
|
46
|
+
/** Send now (also used before publishing). Resolves when idle. */
|
|
47
|
+
async flush() {
|
|
48
|
+
if (this.timer) {
|
|
49
|
+
this.clearTimer(this.timer);
|
|
50
|
+
this.timer = null;
|
|
51
|
+
}
|
|
52
|
+
if (this.inFlight) {
|
|
53
|
+
await this.inFlight;
|
|
54
|
+
if (this.pending)
|
|
55
|
+
return this.flush();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (!this.pending || this.state.status === 'conflict')
|
|
59
|
+
return;
|
|
60
|
+
const patch = this.pending;
|
|
61
|
+
this.pending = null;
|
|
62
|
+
this.setState({ status: 'saving' });
|
|
63
|
+
this.inFlight = (async () => {
|
|
64
|
+
let result;
|
|
65
|
+
try {
|
|
66
|
+
result = await this.opts.send(patch);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
result = { ok: false, status: 0, message: err instanceof Error ? err.message : String(err) };
|
|
70
|
+
}
|
|
71
|
+
this.inFlight = null;
|
|
72
|
+
if (result.ok) {
|
|
73
|
+
this.attempt = 0;
|
|
74
|
+
this.setState({
|
|
75
|
+
status: this.pending ? 'pending' : 'saved',
|
|
76
|
+
lastSavedAt: Date.now(),
|
|
77
|
+
message: undefined,
|
|
78
|
+
seq: this.state.seq + 1,
|
|
79
|
+
});
|
|
80
|
+
if (this.pending)
|
|
81
|
+
this.arm(0);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (result.status === 409) {
|
|
85
|
+
this.pending = null;
|
|
86
|
+
this.setState({ status: 'conflict', message: result.message });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Put the failed patch back underneath anything newer.
|
|
90
|
+
this.pending = { ...patch, ...(this.pending ?? {}) };
|
|
91
|
+
if (isTransient(result.status) && this.attempt < this.retries) {
|
|
92
|
+
this.attempt += 1;
|
|
93
|
+
this.setState({ status: 'pending', message: result.message });
|
|
94
|
+
this.arm(1000 * 2 ** (this.attempt - 1));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this.attempt = 0;
|
|
98
|
+
this.setState({
|
|
99
|
+
status: 'error',
|
|
100
|
+
message: result.message ?? `Save failed (${result.status})`,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
})();
|
|
104
|
+
await this.inFlight;
|
|
105
|
+
}
|
|
106
|
+
/** Clear a conflict or error (after the caller reloaded the document). Drops unsent changes. */
|
|
107
|
+
reset() {
|
|
108
|
+
if (this.timer)
|
|
109
|
+
this.clearTimer(this.timer);
|
|
110
|
+
this.timer = null;
|
|
111
|
+
this.pending = null;
|
|
112
|
+
this.attempt = 0;
|
|
113
|
+
this.setState({ status: 'idle', message: undefined });
|
|
114
|
+
}
|
|
115
|
+
/** Retry after an error, keeping the unsent changes. */
|
|
116
|
+
retry() {
|
|
117
|
+
if (this.state.status !== 'error')
|
|
118
|
+
return;
|
|
119
|
+
this.attempt = 0;
|
|
120
|
+
this.arm(0);
|
|
121
|
+
}
|
|
122
|
+
/** Stop timers (unmount). Unsent changes are dropped; call flush() first to keep them. */
|
|
123
|
+
dispose() {
|
|
124
|
+
if (this.timer)
|
|
125
|
+
this.clearTimer(this.timer);
|
|
126
|
+
this.timer = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=autosave.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autosave.js","sourceRoot":"","sources":["../src/autosave.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Defaults shared by every package, so routes and names are defined once. */
|
|
2
|
+
export declare const DEFAULTS: {
|
|
3
|
+
readonly apiRoute: "/api";
|
|
4
|
+
readonly routePrefix: "/onpage";
|
|
5
|
+
readonly adminRoute: "/admin";
|
|
6
|
+
};
|
|
7
|
+
/** Cookies and headers used by the Next.js adapter and its proxy. */
|
|
8
|
+
export declare const COOKIE: {
|
|
9
|
+
/** Editor mode, bound to the signed-in session: `edit|view:<authCookie>:<hash>`. */
|
|
10
|
+
readonly mode: "onpage";
|
|
11
|
+
/** Sealed preview-link grant (path, expiry, Next's draft secret); only the proxy can read it. */
|
|
12
|
+
readonly preview: "onpage_preview";
|
|
13
|
+
/** Next.js's draft-mode cookie. */
|
|
14
|
+
readonly nextDraft: "__prerender_bypass";
|
|
15
|
+
};
|
|
16
|
+
export declare const HEADER: {
|
|
17
|
+
/** Request path, set by the proxy (server components can't see it otherwise). */
|
|
18
|
+
readonly path: "x-onpage-path";
|
|
19
|
+
/** Set by the proxy when this request is a preview-link view. */
|
|
20
|
+
readonly shared: "x-onpage-shared";
|
|
21
|
+
/** Stale-write guard: the `updatedAt` the editor last saw. */
|
|
22
|
+
readonly ifUnmodifiedSince: "x-onpage-if-unmodified-since";
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,eAAO,MAAM,QAAQ;;;;CAIX,CAAA;AAEV,qEAAqE;AACrE,eAAO,MAAM,MAAM;IACjB,oFAAoF;;IAEpF,iGAAiG;;IAEjG,mCAAmC;;CAE3B,CAAA;AAEV,eAAO,MAAM,MAAM;IACjB,iFAAiF;;IAEjF,iEAAiE;;IAEjE,8DAA8D;;CAEtD,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Defaults shared by every package, so routes and names are defined once. */
|
|
2
|
+
export const DEFAULTS = {
|
|
3
|
+
apiRoute: '/api',
|
|
4
|
+
routePrefix: '/onpage',
|
|
5
|
+
adminRoute: '/admin',
|
|
6
|
+
};
|
|
7
|
+
/** Cookies and headers used by the Next.js adapter and its proxy. */
|
|
8
|
+
export const COOKIE = {
|
|
9
|
+
/** Editor mode, bound to the signed-in session: `edit|view:<authCookie>:<hash>`. */
|
|
10
|
+
mode: 'onpage',
|
|
11
|
+
/** Sealed preview-link grant (path, expiry, Next's draft secret); only the proxy can read it. */
|
|
12
|
+
preview: 'onpage_preview',
|
|
13
|
+
/** Next.js's draft-mode cookie. */
|
|
14
|
+
nextDraft: '__prerender_bypass',
|
|
15
|
+
};
|
|
16
|
+
export const HEADER = {
|
|
17
|
+
/** Request path, set by the proxy (server components can't see it otherwise). */
|
|
18
|
+
path: 'x-onpage-path',
|
|
19
|
+
/** Set by the proxy when this request is a preview-link view. */
|
|
20
|
+
shared: 'x-onpage-shared',
|
|
21
|
+
/** Stale-write guard: the `updatedAt` the editor last saw. */
|
|
22
|
+
ifUnmodifiedSince: 'x-onpage-if-unmodified-since',
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,QAAQ,EAAE,MAAM;IAChB,WAAW,EAAE,SAAS;IACtB,UAAU,EAAE,QAAQ;CACZ,CAAA;AAEV,qEAAqE;AACrE,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,oFAAoF;IACpF,IAAI,EAAE,QAAQ;IACd,iGAAiG;IACjG,OAAO,EAAE,gBAAgB;IACzB,mCAAmC;IACnC,SAAS,EAAE,oBAAoB;CACvB,CAAA;AAEV,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,iFAAiF;IACjF,IAAI,EAAE,eAAe;IACrB,iEAAiE;IACjE,MAAM,EAAE,iBAAiB;IACzB,8DAA8D;IAC9D,iBAAiB,EAAE,8BAA8B;CACzC,CAAA"}
|
package/dist/docref.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A reference to one editable Payload document: a collection document or a
|
|
3
|
+
* global. Every target on the page carries one, so a single session can edit
|
|
4
|
+
* the page, its header global and any embedded document at once.
|
|
5
|
+
*/
|
|
6
|
+
export type DocRef = {
|
|
7
|
+
kind: 'collection';
|
|
8
|
+
slug: string;
|
|
9
|
+
id: string | number;
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'global';
|
|
12
|
+
slug: string;
|
|
13
|
+
};
|
|
14
|
+
/** Stable string key: "c:<slug>:<id>" or "g:<slug>". */
|
|
15
|
+
export declare function refKey(ref: DocRef): string;
|
|
16
|
+
export declare function parseRefKey(key: string): DocRef | null;
|
|
17
|
+
/** Key for one field (or row/list) of one document: "<refKey>|<path>". */
|
|
18
|
+
export declare const targetKey: (ref: DocRef | string, path: string) => string;
|
|
19
|
+
//# sourceMappingURL=docref.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docref.d.ts","sourceRoot":"","sources":["../src/docref.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,MAAM,GAChB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9F,wDAAwD;AACxD,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE1C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CActD;AAED,0EAA0E;AAC1E,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,GAAG,MAAM,EAAE,MAAM,MAAM,KAAG,MACL,CAAA"}
|
package/dist/docref.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Stable string key: "c:<slug>:<id>" or "g:<slug>". */
|
|
2
|
+
export function refKey(ref) {
|
|
3
|
+
return ref.kind === 'global' ? `g:${ref.slug}` : `c:${ref.slug}:${ref.id}`;
|
|
4
|
+
}
|
|
5
|
+
export function parseRefKey(key) {
|
|
6
|
+
if (key.startsWith('g:')) {
|
|
7
|
+
const slug = key.slice(2);
|
|
8
|
+
return slug ? { kind: 'global', slug } : null;
|
|
9
|
+
}
|
|
10
|
+
if (key.startsWith('c:')) {
|
|
11
|
+
const rest = key.slice(2);
|
|
12
|
+
const i = rest.indexOf(':');
|
|
13
|
+
if (i <= 0 || i === rest.length - 1)
|
|
14
|
+
return null;
|
|
15
|
+
const slug = rest.slice(0, i);
|
|
16
|
+
const raw = rest.slice(i + 1);
|
|
17
|
+
return { kind: 'collection', slug, id: /^\d+$/.test(raw) ? Number(raw) : raw };
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
/** Key for one field (or row/list) of one document: "<refKey>|<path>". */
|
|
22
|
+
export const targetKey = (ref, path) => `${typeof ref === 'string' ? ref : refKey(ref)}|${path}`;
|
|
23
|
+
//# sourceMappingURL=docref.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docref.js","sourceRoot":"","sources":["../src/docref.ts"],"names":[],"mappings":"AAQA,wDAAwD;AACxD,MAAM,UAAU,MAAM,CAAC,GAAW;IAChC,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,EAAE,CAAA;AAC5E,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACzB,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAC/C,CAAC;IACD,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACzB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QAC7B,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;IAChF,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAoB,EAAE,IAAY,EAAU,EAAE,CACtE,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAA"}
|
package/dist/encode.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SchemaField } from './schema.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Does this field name suggest a machine value? Matches whole camelCase/snake
|
|
4
|
+
* segments: "ctaUrl", "brand_color", "slug" yes; "lifestyleTitle", "keynote" no.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isMachineFieldName(name: string): boolean;
|
|
7
|
+
export type EncodeFilter = (args: {
|
|
8
|
+
field: SchemaField;
|
|
9
|
+
path: string;
|
|
10
|
+
ref: string;
|
|
11
|
+
}) => boolean;
|
|
12
|
+
/** Return a copy of `doc` with markers on the given (editable) top-level fields. */
|
|
13
|
+
export declare function encodeDocument<T extends Record<string, unknown>>(doc: T, fields: SchemaField[], ref: string, opts?: {
|
|
14
|
+
only?: string[];
|
|
15
|
+
filter?: EncodeFilter;
|
|
16
|
+
}): T;
|
|
17
|
+
//# sourceMappingURL=encode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../src/encode.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AA0B9C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIxD;AAKD,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAA;AAwE/F,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9D,GAAG,EAAE,CAAC,EACN,MAAM,EAAE,WAAW,EAAE,EACrB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,YAAY,CAAA;CAAO,GACpD,CAAC,CAGH"}
|
package/dist/encode.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-mapping: tag a document's displayable strings with stega
|
|
3
|
+
* markers (see @payload-onpage/core stega) so the editor can find them on the
|
|
4
|
+
* page. The plugin runs it on reads that ask for it (`context.onpage`);
|
|
5
|
+
* client mode runs it on the editor state it renders.
|
|
6
|
+
*
|
|
7
|
+
* Tagged: text and textarea values, rich-text text nodes, and the `alt` of
|
|
8
|
+
* populated uploads (carrying the parent's upload-field path, so `<img alt>`
|
|
9
|
+
* maps an image). Never tagged: selects, numbers, dates, relationships, JSON,
|
|
10
|
+
* code, and any field whose name suggests machine use (url, slug, email…).
|
|
11
|
+
*/
|
|
12
|
+
import { stegaEncode } from "./stega.js";
|
|
13
|
+
import { blockFor } from "./fields.js";
|
|
14
|
+
import { mapLexical } from "./lexical.js";
|
|
15
|
+
/** Name segments that mark a field as holding a machine value (never marked). */
|
|
16
|
+
const MACHINE_WORDS = new Set([
|
|
17
|
+
'url',
|
|
18
|
+
'href',
|
|
19
|
+
'link',
|
|
20
|
+
'path',
|
|
21
|
+
'email',
|
|
22
|
+
'phone',
|
|
23
|
+
'slug',
|
|
24
|
+
'key',
|
|
25
|
+
'token',
|
|
26
|
+
'anchor',
|
|
27
|
+
'id',
|
|
28
|
+
'class',
|
|
29
|
+
'classname',
|
|
30
|
+
'style',
|
|
31
|
+
'icon',
|
|
32
|
+
'color',
|
|
33
|
+
'colour',
|
|
34
|
+
'hex',
|
|
35
|
+
]);
|
|
36
|
+
/**
|
|
37
|
+
* Does this field name suggest a machine value? Matches whole camelCase/snake
|
|
38
|
+
* segments: "ctaUrl", "brand_color", "slug" yes; "lifestyleTitle", "keynote" no.
|
|
39
|
+
*/
|
|
40
|
+
export function isMachineFieldName(name) {
|
|
41
|
+
return name
|
|
42
|
+
.split(/(?<=[a-z0-9])(?=[A-Z])|[_\-\s]+/)
|
|
43
|
+
.some((w) => MACHINE_WORDS.has(w.toLowerCase()));
|
|
44
|
+
}
|
|
45
|
+
/** Field editors that edit text: a field using any other editor (color, …) holds a machine value and is never marked. */
|
|
46
|
+
const TEXT_EDITORS = new Set(['text', 'textarea', 'email', 'richText']);
|
|
47
|
+
const allowed = (field, path, ctx) => !isMachineFieldName(field.name) &&
|
|
48
|
+
(!field.editor || TEXT_EDITORS.has(field.editor)) &&
|
|
49
|
+
(ctx.filter ? ctx.filter({ field, path, ref: ctx.ref }) : true);
|
|
50
|
+
const mark = (value, ctx, path, kind) => stegaEncode(value, { ref: ctx.ref, path, kind });
|
|
51
|
+
function encodeRichText(value, ctx, path) {
|
|
52
|
+
return mapLexical(value, (node) => node.type === 'text' && typeof node.text === 'string' && node.text
|
|
53
|
+
? { ...node, text: mark(node.text, ctx, path, 'richtext') }
|
|
54
|
+
: node);
|
|
55
|
+
}
|
|
56
|
+
function encodeValue(value, field, path, ctx) {
|
|
57
|
+
if (value == null)
|
|
58
|
+
return value;
|
|
59
|
+
switch (field.type) {
|
|
60
|
+
case 'text':
|
|
61
|
+
case 'textarea':
|
|
62
|
+
if (typeof value === 'string' && value && allowed(field, path, ctx))
|
|
63
|
+
return mark(value, ctx, path, field.type === 'textarea' ? 'textarea' : 'text');
|
|
64
|
+
return value;
|
|
65
|
+
case 'richText':
|
|
66
|
+
return allowed(field, path, ctx) ? encodeRichText(value, ctx, path) : value;
|
|
67
|
+
case 'upload':
|
|
68
|
+
if (!field.hasMany && value && typeof value === 'object' && !Array.isArray(value)) {
|
|
69
|
+
const media = value;
|
|
70
|
+
return {
|
|
71
|
+
...media,
|
|
72
|
+
alt: mark(typeof media.alt === 'string' ? media.alt : '', ctx, path, 'media'),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
case 'group':
|
|
77
|
+
return encodeFields(value, field.fields ?? [], path, ctx);
|
|
78
|
+
case 'array':
|
|
79
|
+
return Array.isArray(value)
|
|
80
|
+
? value.map((row, i) => encodeFields(row, field.fields ?? [], `${path}.${i}`, ctx))
|
|
81
|
+
: value;
|
|
82
|
+
case 'blocks':
|
|
83
|
+
return Array.isArray(value)
|
|
84
|
+
? value.map((row, i) => {
|
|
85
|
+
const block = blockFor(field, row);
|
|
86
|
+
return block ? encodeFields(row, block.fields, `${path}.${i}`, ctx) : row;
|
|
87
|
+
})
|
|
88
|
+
: value;
|
|
89
|
+
default:
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function encodeFields(data, fields, prefix, ctx) {
|
|
94
|
+
if (!data || typeof data !== 'object' || Array.isArray(data))
|
|
95
|
+
return data;
|
|
96
|
+
const out = { ...data };
|
|
97
|
+
for (const f of fields) {
|
|
98
|
+
if (!(f.name in out))
|
|
99
|
+
continue;
|
|
100
|
+
out[f.name] = encodeValue(out[f.name], f, prefix ? `${prefix}.${f.name}` : f.name, ctx);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
/** Return a copy of `doc` with markers on the given (editable) top-level fields. */
|
|
105
|
+
export function encodeDocument(doc, fields, ref, opts = {}) {
|
|
106
|
+
const scope = opts.only ? fields.filter((f) => opts.only.includes(f.name)) : fields;
|
|
107
|
+
return encodeFields(doc, scope, '', { ref, filter: opts.filter });
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=encode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encode.js","sourceRoot":"","sources":["../src/encode.ts"],"names":[],"mappings":"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"}
|
package/dist/fields.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema-aware operations on document data: find the field behind a path, build
|
|
3
|
+
* new blocks/rows from defaults, clone rows with fresh ids, and normalize data
|
|
4
|
+
* for saving (populated relations back to ids).
|
|
5
|
+
*/
|
|
6
|
+
import { type PathSegment } from './path.ts';
|
|
7
|
+
import type { DocumentSchema, SchemaBlock, SchemaField } from './schema.ts';
|
|
8
|
+
export interface FieldAtPath {
|
|
9
|
+
field: SchemaField;
|
|
10
|
+
/** The block whose fields contain this field, when inside a blocks row. */
|
|
11
|
+
block?: SchemaBlock;
|
|
12
|
+
/** Path of the nearest blocks row ("sections.3"), when inside one. */
|
|
13
|
+
blockPath?: string;
|
|
14
|
+
}
|
|
15
|
+
/** The block a blocks-field row uses (by its `blockType`). */
|
|
16
|
+
export declare const blockFor: (field: SchemaField, row: unknown) => SchemaBlock | undefined;
|
|
17
|
+
/** May the editor change this path? (Its top-level field must be editable.) */
|
|
18
|
+
export declare const isEditablePath: (schema: DocumentSchema, path: string) => boolean;
|
|
19
|
+
export interface RowInfo {
|
|
20
|
+
/** "layout.2" */
|
|
21
|
+
path: string;
|
|
22
|
+
/** "layout" */
|
|
23
|
+
listPath: string;
|
|
24
|
+
index: number;
|
|
25
|
+
length: number;
|
|
26
|
+
listField: SchemaField;
|
|
27
|
+
/** Fields of this row: the block's fields, or the array's. */
|
|
28
|
+
rowFields: SchemaField[];
|
|
29
|
+
/** Singular label of one row ("Hero", "Feature"). */
|
|
30
|
+
label: string;
|
|
31
|
+
isBlock: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** Everything the editor needs about one row of an array or blocks field, or null if `path` isn't an editable row. */
|
|
34
|
+
export declare function resolveRow(schema: DocumentSchema, doc: unknown, path: string): RowInfo | null;
|
|
35
|
+
/**
|
|
36
|
+
* Walk the schema along `path`, using the document to resolve block types.
|
|
37
|
+
* Returns the field at the end of the path (for an index segment, the array or
|
|
38
|
+
* blocks field that owns the row).
|
|
39
|
+
*/
|
|
40
|
+
export declare function fieldAtPath(fields: SchemaField[], doc: unknown, path: string | PathSegment[]): FieldAtPath | null;
|
|
41
|
+
/** Defaults for a set of fields: static `defaultValue`s, nested groups, `minRows` rows. */
|
|
42
|
+
export declare function defaultsFor(fields: SchemaField[]): Record<string, unknown>;
|
|
43
|
+
export declare function newArrayRow(arrayField: SchemaField): Record<string, unknown>;
|
|
44
|
+
export declare function newBlockRow(block: SchemaBlock): Record<string, unknown>;
|
|
45
|
+
/**
|
|
46
|
+
* Deep-clone a row (array item or block) giving it, and every nested row, a new
|
|
47
|
+
* id. Ids of related documents (uploads, relationships) are left alone, so a
|
|
48
|
+
* duplicated card still points at the same image.
|
|
49
|
+
*/
|
|
50
|
+
export declare function cloneRow(row: unknown, rowFields: SchemaField[]): unknown;
|
|
51
|
+
/** The id of a relationship value: a populated doc, `{ relationTo, value }`, or an id. */
|
|
52
|
+
export declare function relationId(v: unknown): string | number | undefined;
|
|
53
|
+
/** A display title for a relationship value (`titleField` when known, else common title fields, else the id). */
|
|
54
|
+
export declare function relationTitle(v: unknown, titleField?: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* Turn rendered (populated) data back into what Payload expects on write:
|
|
57
|
+
* uploads and relationships become ids, everything else passes through.
|
|
58
|
+
*/
|
|
59
|
+
export declare function normalizeValue(value: unknown, field: SchemaField): unknown;
|
|
60
|
+
/**
|
|
61
|
+
* Rich text read at depth > 0 has populated relations inside nodes (an upload
|
|
62
|
+
* node's `value`, an internal link's `fields.doc.value`). Payload stores ids, so
|
|
63
|
+
* those go back to ids; nothing else in the tree changes.
|
|
64
|
+
*/
|
|
65
|
+
export declare function normalizeRichText(value: unknown): unknown;
|
|
66
|
+
/** Normalize the given top-level keys of a document for a PATCH body. */
|
|
67
|
+
export declare function buildPatch(doc: Record<string, unknown>, keys: string[], fields: SchemaField[]): Record<string, unknown>;
|
|
68
|
+
/** Top-level keys (from `keys`) whose values differ between two documents. */
|
|
69
|
+
export declare function changedKeys(prev: Record<string, unknown>, next: Record<string, unknown>, keys: string[]): string[];
|
|
70
|
+
/** A scalar value as editable text: strings and numbers as-is; null, undefined and objects as ''. */
|
|
71
|
+
export declare function toText(value: unknown): string;
|
|
72
|
+
//# sourceMappingURL=fields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"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"}
|