@plitzi/sdk-schema 0.37.8 → 0.37.9

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.
@@ -0,0 +1,68 @@
1
+ import { default as FlatMap } from './FlatMap';
2
+ import { MintElementId } from './elementId';
3
+ import { Element, Schema, SpaceComponent, SpaceComponentDeclaration } from '@plitzi/sdk-shared';
4
+ /** The parts of a schema that hold elements: the pages' tree and every component's. */
5
+ export type ElementDocument = Pick<Schema, 'flat' | 'pages' | 'components'>;
6
+ /** One tree of a document: the space's own `flat`, or the one of the component named. */
7
+ export type ElementTree = {
8
+ flat: Schema['flat'];
9
+ componentId?: SpaceComponent['id'];
10
+ };
11
+ /**
12
+ * Attributes an instance already answers to, so a prop may not be called one of them: a prop IS an attribute of the
13
+ * instance, and a prop named `referenceId` would be the instance pointing at another component.
14
+ */
15
+ export declare const RESERVED_PROP_NAMES: ReadonlySet<string>;
16
+ /** Why `name` cannot be a prop, phrased for a person, or null when it can. */
17
+ export declare const propNameProblem: (name: string) => string | null;
18
+ export declare const treesOf: (schema: Pick<Schema, "flat" | "components">) => ElementTree[];
19
+ /** The tree `id` lives in. Ids are one namespace across the document, so there is at most one. */
20
+ export declare const treeOf: (schema: Pick<Schema, "flat" | "components">, id: Element["id"]) => ElementTree | undefined;
21
+ /** Every element id the document holds, in any tree. */
22
+ export declare const documentIds: (schema: Pick<Schema, "flat" | "components">) => Set<Element["id"]>;
23
+ /**
24
+ * A `FlatMap` over the tree that holds `id`, held to the ids every other tree holds.
25
+ *
26
+ * What lets an element edit carry nothing but the element: its id already says which tree it is in, so the writers
27
+ * of a space — the builder's reducer and the server's model — resolve the tree here and edit it as they always have.
28
+ */
29
+ export declare const flatMapOf: (schema: ElementDocument, id: Element["id"], mintId?: MintElementId) => FlatMap | undefined;
30
+ /** The component declared under `componentId`, if any: a key read off a document cannot be trusted to be there. */
31
+ export declare const componentNamed: (schema: Pick<Schema, "components">, componentId: SpaceComponent["id"]) => SpaceComponent | undefined;
32
+ /** Whether `element` is an instance — of `componentId`, when one is named. */
33
+ export declare const isInstance: (element: Element, componentId?: SpaceComponent["id"]) => boolean;
34
+ /** Every instance of a component, wherever it is placed: on a page, in a layout or inside another component. */
35
+ export declare const instancesOf: (schema: Pick<Schema, "flat" | "components">, componentId: SpaceComponent["id"]) => Element[];
36
+ /**
37
+ * Renames an element wherever it is, carrying every reference to it — in any tree.
38
+ *
39
+ * The element's own tree is rewritten by `FlatMap`, as a rename always was; the others are rewritten too, because a
40
+ * page can name an element of a component as little as a component can name a page, and the declarations name their
41
+ * roots and slots. Returns the ids of everything it touched, under their new names.
42
+ */
43
+ export declare const renameElement: (schema: ElementDocument, from: Element["id"], to: Element["id"]) => Element["id"][] | false;
44
+ /**
45
+ * Declares a component.
46
+ *
47
+ * With `from`, the component is made OF an element already in the document: the subtree leaves its tree for the
48
+ * component's, and an instance named `from.instanceId` takes its place, so the page renders what it rendered. Without
49
+ * it, the component brings its own tree. Both sides of a live document apply the same call and arrive at the same
50
+ * document, which is why the instance's name is the caller's to give. Returns false, changing nothing, when the
51
+ * component cannot be declared — a name taken, a tree that is not one, a declaration its tree does not bear out.
52
+ */
53
+ export declare const addComponent: (schema: ElementDocument, component: SpaceComponent, from?: {
54
+ elementId: Element["id"];
55
+ instanceId: Element["id"];
56
+ }) => boolean;
57
+ /** Changes what a component declares — never its tree, which is edited element by element like any other. */
58
+ export declare const updateComponent: (schema: Pick<Schema, "components">, componentId: SpaceComponent["id"], declaration: SpaceComponentDeclaration) => boolean;
59
+ /** Removes a component nothing places any more. One still placed is refused: its instances would render nothing. */
60
+ export declare const removeComponent: (schema: Pick<Schema, "flat" | "components">, componentId: SpaceComponent["id"]) => boolean;
61
+ /**
62
+ * Replaces an instance with a copy of what its component renders, which is then the page's own to edit.
63
+ *
64
+ * The copy takes names derived from the component's (`card` → `card-2`), each the first one free in the document,
65
+ * so every side of a live document that applies it arrives at the same names. What filled the instance's slots goes
66
+ * into the copies of those slots. Refused when a child has no slot to go to. Returns the copy's root id.
67
+ */
68
+ export declare const detachInstance: (schema: ElementDocument, instanceId: Element["id"]) => Element["id"] | false;
@@ -0,0 +1,130 @@
1
+ import { isValidElementId as e, repointIds as t, uniqueElementId as n } from "./elementId.mjs";
2
+ import { descendants as r } from "./elementTree.mjs";
3
+ import i from "./FlatMap.mjs";
4
+ import { jsonCopy as a } from "@plitzi/sdk-shared/history";
5
+ //#region src/helpers/components.ts
6
+ var o = /* @__PURE__ */ new Set([
7
+ "referenceType",
8
+ "referenceId",
9
+ "slot",
10
+ "className",
11
+ "style",
12
+ "children",
13
+ "ref",
14
+ "id"
15
+ ]), s = /^[A-Za-z][A-Za-z0-9_]*$/, c = (e) => s.test(e) ? o.has(e) ? `"${e}" is an attribute every instance already has` : null : `"${e}" is not a valid prop name: start with a letter, then letters, numbers and underscores — it is read as {{ props.${e} }}`, l = (e) => [{ flat: e.flat }, ...Object.values(e.components).map((e) => ({
16
+ flat: e.flat,
17
+ componentId: e.id
18
+ }))], u = (e, t) => l(e).find((e) => Object.hasOwn(e.flat, t)), d = (e) => new Set(l(e).flatMap((e) => Object.keys(e.flat))), f = (e, t, n) => {
19
+ let r = u(e, t);
20
+ if (!r) return;
21
+ let a = l(e).filter((e) => e.flat !== r.flat);
22
+ return new i({
23
+ flat: r.flat,
24
+ pages: r.componentId ? void 0 : e.pages,
25
+ mintId: n,
26
+ takenElsewhere: (e) => a.some((t) => Object.hasOwn(t.flat, e))
27
+ });
28
+ }, p = (e, t) => Object.hasOwn(e.components, t) ? e.components[t] : void 0, m = (e, t) => e.definition.type === "reference" && e.attributes.referenceType === "component" && (t === void 0 || e.attributes.referenceId === t), h = (e, t) => l(e).flatMap((e) => Object.values(e.flat).filter((e) => m(e, t))), g = (e, n, r) => {
29
+ let i = f(e, n);
30
+ if (!i) return !1;
31
+ let a = i.renameElement(n, r);
32
+ if (!a || n === r) return a;
33
+ for (let o of l(e)) o.flat !== i.flat && a.push(...t(o.flat, { [n]: r }));
34
+ for (let t of Object.values(e.components)) t.rootId === n && (t.rootId = r), t.slots?.includes(n) && (t.slots = t.slots.map((e) => e === n ? r : e));
35
+ return [...new Set(a)];
36
+ }, _ = (e, t) => {
37
+ for (let t of Object.keys(e.props ?? {})) {
38
+ let e = c(t);
39
+ if (e) return e;
40
+ }
41
+ let n = (e.slots ?? []).find((e) => !Object.hasOwn(t, e));
42
+ return n ? `slot "${n}" is not an element of the component` : null;
43
+ }, v = (e, t, n) => ({
44
+ id: t,
45
+ attributes: {
46
+ referenceType: "component",
47
+ referenceId: e.id
48
+ },
49
+ definition: {
50
+ label: e.label ?? e.id,
51
+ type: "reference",
52
+ items: [],
53
+ bindings: {},
54
+ parentId: n.definition.parentId,
55
+ rootId: n.definition.rootId,
56
+ styleSelectors: { base: "" },
57
+ initialState: { visibility: !0 }
58
+ }
59
+ }), y = (t, n, i) => {
60
+ if (!e(n.id) || Object.hasOwn(t.components, n.id)) return !1;
61
+ if (!i) {
62
+ let e = n.flat[n.rootId], r = d(t);
63
+ return !e || e.definition.parentId || Object.keys(n.flat).some((e) => r.has(e)) || _(n, n.flat) ? !1 : (t.components[n.id] = n, !0);
64
+ }
65
+ let { elementId: a, instanceId: o } = i, s = u(t, a), c = s?.flat[a], l = c?.definition.parentId ? s?.flat[c.definition.parentId] : void 0;
66
+ if (!s || !c || !l?.definition.items || !e(o) || d(t).has(o)) return !1;
67
+ let f = {};
68
+ for (let e of [a, ...r(s.flat, a)]) {
69
+ let { parentId: t, ...n } = s.flat[e].definition;
70
+ f[e] = {
71
+ ...s.flat[e],
72
+ definition: {
73
+ ...n,
74
+ ...e === a ? {} : { parentId: t },
75
+ rootId: a
76
+ }
77
+ };
78
+ }
79
+ let p = {
80
+ ...n,
81
+ rootId: a,
82
+ flat: f
83
+ };
84
+ if (_(p, f)) return !1;
85
+ for (let e of Object.keys(f)) delete s.flat[e];
86
+ return s.flat[o] = v(p, o, c), l.definition.items = l.definition.items.map((e) => e === a ? o : e), t.components[p.id] = p, !0;
87
+ }, b = (e, t, n) => {
88
+ let r = p(e, t);
89
+ return !r || _({
90
+ ...r,
91
+ ...n
92
+ }, r.flat) ? !1 : (e.components[t] = {
93
+ ...r,
94
+ ...n
95
+ }, !0);
96
+ }, x = (e, t) => !Object.hasOwn(e.components, t) || h(e, t).length > 0 ? !1 : (delete e.components[t], !0), S = (e, i) => {
97
+ let o = u(e, i), s = o?.flat[i], { referenceId: c } = s?.attributes ?? {}, l = s && m(s) && typeof c == "string" ? p(e, c) : void 0, f = s?.definition.parentId ? o?.flat[s.definition.parentId] : void 0;
98
+ if (!o || !s || !l || !f?.definition.items) return !1;
99
+ let h = s.definition.items ?? [], g = (e) => {
100
+ let { slot: t } = o.flat[e].attributes;
101
+ return typeof t == "string" ? t : l.slots?.[0];
102
+ };
103
+ if (h.some((e) => !l.slots?.includes(g(e) ?? ""))) return !1;
104
+ let _ = d(e);
105
+ _.delete(i);
106
+ let v = [l.rootId, ...r(l.flat, l.rootId)], y = {};
107
+ for (let e of v) y[e] = n(e, (e) => _.has(e)), _.add(y[e]);
108
+ let b = {};
109
+ for (let e of v) {
110
+ let t = a(l.flat[e]);
111
+ t.definition.rootId = s.definition.rootId, b[e] = t;
112
+ }
113
+ t(b, y);
114
+ let x = b[y[l.rootId]];
115
+ x.definition.parentId = s.definition.parentId;
116
+ for (let e of h) {
117
+ let t = b[y[g(e) ?? ""]], n = o.flat[e], { slot: r, ...i } = n.attributes;
118
+ o.flat[e] = {
119
+ ...n,
120
+ attributes: i,
121
+ definition: {
122
+ ...n.definition,
123
+ parentId: t.id
124
+ }
125
+ }, t.definition.items = [...t.definition.items ?? [], e];
126
+ }
127
+ return Object.assign(o.flat, b), delete o.flat[i], f.definition.items = f.definition.items.map((e) => e === i ? x.id : e), x.id;
128
+ };
129
+ //#endregion
130
+ export { o as RESERVED_PROP_NAMES, y as addComponent, p as componentNamed, S as detachInstance, d as documentIds, f as flatMapOf, h as instancesOf, m as isInstance, c as propNameProblem, x as removeComponent, g as renameElement, u as treeOf, l as treesOf, b as updateComponent };
@@ -11,9 +11,9 @@ export declare const isValidElementId: (id: string) => boolean;
11
11
  * collapse, and a leading non-letter is dropped — so a name is editable as prose in the builder's tree and still
12
12
  * lands as a valid key. Empty when nothing usable survives, which the caller reports rather than storing. */
13
13
  export declare const slugifyElementId: (value: string) => string;
14
- /** Every id currently in use, so a new one can be checked or minted against it. Uniqueness is per DOCUMENT — a
15
- * space schema, and each segment separately — which is what makes a lookup a `flat` key access. A segment used on
16
- * several pages repeats its ids in the rendered tree; `rootId`/`referenceId` are what tell those apart. */
14
+ /** Every id in use in one tree, so a new one can be checked or minted against it. Uniqueness spans the DOCUMENT —
15
+ * the space's `flat` and every component's (`documentIds`) — which is what makes a lookup a `flat` key access. A
16
+ * component placed several times repeats its ids in the rendered tree; each instance's scope tells those apart. */
17
17
  export declare const takenIds: (flat: Record<Element["id"], Element>) => Set<string>;
18
18
  /** Why `id` cannot be used in this document, phrased for a person, or null when it is free and well formed.
19
19
  * `ignoreElementId` exempts the element being edited, so re-saving an element its own id is not a conflict. */
@@ -78,6 +78,8 @@ var e = /^[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9_])?$/, t = (t) => e.test(t), n = (
78
78
  let r = o[n];
79
79
  typeof r == "string" && t[r] && (!i || i(o)) && (o[n] = t[r], e = !0);
80
80
  }
81
+ let { slot: s } = o;
82
+ typeof s == "string" && t[s] && (o.slot = t[s], e = !0);
81
83
  for (let n of Object.values(r.bindings ?? {})) for (let r of n) {
82
84
  let n = p(r.source, t);
83
85
  n !== r.source && (r.source = n, e = !0), r.transformers && g(r.transformers, t) && (e = !0), r.when && g(r.when, t) && (e = !0);
@@ -1,5 +1,6 @@
1
1
  import { Schema } from '@plitzi/sdk-shared';
2
2
  export type SchemaValidationOptions = {
3
+ /** Validates `flat` as a fragment hung off this element — a component's tree — instead of as the pages' tree. */
3
4
  baseElementId?: string;
4
5
  /**
5
6
  * Element type → the source name it publishes under.
@@ -14,6 +15,8 @@ export type SchemaValidationError = {
14
15
  code: string;
15
16
  message: string;
16
17
  elementId?: string;
18
+ /** The component whose tree the error is in; absent for the pages' tree. */
19
+ componentId?: string;
17
20
  details?: unknown;
18
21
  };
19
22
  export type SchemaValidationResult = {