@panphora/clayjs 0.2.1 → 0.4.1
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/README.md +103 -7
- package/clay-data.js +1 -1
- package/clay-internals.js +19 -0
- package/clay.js +19 -3
- package/package.json +5 -2
- package/sap.js +5 -5
- package/src/attrs/onaftersave.js +12 -1
- package/src/attrs/refetch-on-save.js +4 -4
- package/src/attrs/save-freeze.js +47 -19
- package/src/core/admin-contenteditable.js +2 -2
- package/src/core/admin-inputs.js +2 -2
- package/src/core/admin-onclick.js +2 -2
- package/src/core/admin-resources.js +23 -14
- package/src/core/autosave.js +11 -1
- package/src/core/edit-mode.js +2 -2
- package/src/core/host-attrs.js +46 -0
- package/src/core/is-edit-mode.js +14 -6
- package/src/core/persist.js +12 -2
- package/src/core/save-core.js +199 -290
- package/src/core/save.js +121 -82
- package/src/core/snapshot.js +89 -36
- package/src/core/unsaved-warning.js +8 -2
- package/src/dom/all.js +4 -29
- package/src/dom/form-data.js +1 -1
- package/src/dom/nearest.js +11 -9
- package/src/internals/index.js +69 -0
- package/src/lib/cache-bust.js +10 -0
- package/src/lib/cookie.js +50 -15
- package/src/lib/extension-noise.js +5 -1
- package/src/lib/mutation.js +65 -18
- package/src/lib/region-policy.js +17 -2
- package/src/lib/root-attrs.js +45 -0
- package/src/lib/throttle.js +25 -13
- package/src/loader.js +18 -24
- package/src/options/options.js +1 -3
- package/src/plugins/demo.js +7 -1
- package/src/plugins/undo.js +3 -3
- package/src/sync/live-sync.js +77 -76
- package/src/ui/dialogs.js +10 -1
- package/src/ui/index.js +0 -10
- package/src/utils/debounce.js +24 -7
- package/src/vendor/control-serialize.vendor.js +2 -1
- package/src/vendor/hyper-undo.vendor.js +1 -1
- package/src/vendor/hypercms.vendor.js +29 -16
- package/src/vendor/richclay.vendor.js +29 -22
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* host-attrs.js — the attributes a host puts on <html>, and what they mean.
|
|
3
|
+
*
|
|
4
|
+
* These are ephemeral: the host injects them into the response and strips them
|
|
5
|
+
* back out of whatever the client saves, so they never reach disk. clayjs only
|
|
6
|
+
* ever reads them, and reads them here so the two spellings of the save token
|
|
7
|
+
* cannot drift between the edit-mode ladder and the save lane.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { HOST_TOKEN_ATTRS, SAVE_TRANSPORT_ATTR } from "../lib/root-attrs.js";
|
|
11
|
+
|
|
12
|
+
// A host that wants the desktop JSON envelope on its save lane declares it on
|
|
13
|
+
// the root. This replaced a `location.hostname === 'localhost'` sniff, which sent
|
|
14
|
+
// the envelope to every host that happened to be local, including ones whose
|
|
15
|
+
// save lane takes text and answers 415.
|
|
16
|
+
export const DESKTOP_JSON = "desktop-json-v1";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The per-document save token this response carries, or null.
|
|
20
|
+
* @returns {?string}
|
|
21
|
+
*/
|
|
22
|
+
export function saveToken() {
|
|
23
|
+
if (typeof document === "undefined") return null;
|
|
24
|
+
for (const attr of HOST_TOKEN_ATTRS) {
|
|
25
|
+
const value = document.documentElement.getAttribute(attr);
|
|
26
|
+
if (value) return value;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* True when the host handed this response a save token of either spelling.
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
export function hasSaveToken() {
|
|
36
|
+
return saveToken() !== null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The save transport the served document declares, or null.
|
|
41
|
+
* @returns {?string}
|
|
42
|
+
*/
|
|
43
|
+
export function saveTransport() {
|
|
44
|
+
if (typeof document === "undefined") return null;
|
|
45
|
+
return document.documentElement.getAttribute(SAVE_TRANSPORT_ATTR);
|
|
46
|
+
}
|
package/src/core/is-edit-mode.js
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import cookie from "../lib/cookie.js";
|
|
2
2
|
import query from "../lib/query.js";
|
|
3
|
+
import { hasSaveToken } from "./host-attrs.js";
|
|
3
4
|
|
|
4
5
|
// Edit-mode precedence: an explicit ?editmode=true|false URL param wins, then an
|
|
5
6
|
// opt-in window.clayEditMode global (with the legacy window.__hyperclayEditMode
|
|
6
|
-
// still honored as a fallback — htmlclay injects it today), then the
|
|
7
|
-
//
|
|
8
|
-
// htmlclay, any self-saving file) that are
|
|
9
|
-
// cookie; setting it before clayjs loads turns
|
|
7
|
+
// still honored as a fallback — htmlclay injects it today), then a save token the
|
|
8
|
+
// host put on the root, then the platform's isAdminOfCurrentResource cookie. The
|
|
9
|
+
// global is for standalone uses (demos, htmlclay, any self-saving file) that are
|
|
10
|
+
// always editable and have no owner cookie; setting it before clayjs loads turns
|
|
11
|
+
// on the edit-only modules.
|
|
12
|
+
//
|
|
13
|
+
// The token sits above the cookie because it is the stronger claim and often the
|
|
14
|
+
// only one visible: the host minted it for this response and nothing else, while
|
|
15
|
+
// the cookie is ambient. A sandboxed document cannot read the cookie at all, so
|
|
16
|
+
// without this rung a host that sandboxes its own documents can hand one a save
|
|
17
|
+
// token and still watch it render read-only.
|
|
10
18
|
let forcedEditMode = null;
|
|
11
19
|
if (typeof window !== "undefined") {
|
|
12
20
|
if (window.clayEditMode != null) {
|
|
@@ -17,10 +25,10 @@ if (typeof window !== "undefined") {
|
|
|
17
25
|
}
|
|
18
26
|
|
|
19
27
|
const isEditMode = query.editmode
|
|
20
|
-
? query.editmode === "true" // takes precedence over the global and cookie
|
|
28
|
+
? query.editmode === "true" // takes precedence over the global, token and cookie
|
|
21
29
|
: forcedEditMode != null
|
|
22
30
|
? forcedEditMode
|
|
23
|
-
: Boolean(cookie.get("isAdminOfCurrentResource"));
|
|
31
|
+
: hasSaveToken() || Boolean(cookie.get("isAdminOfCurrentResource"));
|
|
24
32
|
|
|
25
33
|
const isOwner = Boolean(cookie.get("isAdminOfCurrentResource"));
|
|
26
34
|
|
package/src/core/persist.js
CHANGED
|
@@ -10,7 +10,10 @@ import { serializeControlToAttributes, finalizeControlForSave } from '../vendor/
|
|
|
10
10
|
//
|
|
11
11
|
// The per-control rules (which attribute represents which control's state) live
|
|
12
12
|
// in control-serialize.vendor.js — the single source of truth shared verbatim
|
|
13
|
-
// with sapjs (carrier mirror) so the two implementations never drift.
|
|
13
|
+
// with sapjs (carrier mirror) so the two implementations never drift. sapjs
|
|
14
|
+
// `npm run propagate` writes this copy and hyperclayjs's from one destination
|
|
15
|
+
// table, and `--check` fails on either being stale, so the invariant is
|
|
16
|
+
// enforced rather than remembered. This
|
|
14
17
|
// module owns only the triggers (which elements, on which events) and the
|
|
15
18
|
// snapshot safety net; serializeControlToAttributes / finalizeControlForSave do
|
|
16
19
|
// the actual attribute writes.
|
|
@@ -86,7 +89,14 @@ export default function enablePersistentFormInputValues(filterBySelector = "[per
|
|
|
86
89
|
const finalize = (selector) => {
|
|
87
90
|
const live = document.querySelectorAll(selector);
|
|
88
91
|
const cloned = doc.querySelectorAll(selector);
|
|
89
|
-
|
|
92
|
+
// Index pairing only holds while the two lists describe the same tree. If a
|
|
93
|
+
// hook ever diverges them, writing one control's value into another's is
|
|
94
|
+
// worse than writing none, so fail loudly instead of silently.
|
|
95
|
+
if (live.length !== cloned.length) {
|
|
96
|
+
console.warn('[persist] live/clone counts differ for', selector, '— skipping');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
cloned.forEach((c, i) => finalizeControlForSave(c, live[i]));
|
|
90
100
|
};
|
|
91
101
|
finalize(inputSelector);
|
|
92
102
|
finalize(textareaSelector);
|