@digital-gravy/etch-public-api 0.2.0 → 0.3.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/README.md CHANGED
@@ -19,26 +19,55 @@ npm install @digital-gravy/etch-public-api
19
19
 
20
20
  ## Usage
21
21
 
22
+ ### Getting the API object
23
+
24
+ The Etch builder injects the runtime onto `window.etch` during its bootstrap.
25
+ Acquire it with `getEtch()`, guarded by `isEtchAvailable()` for code that might
26
+ run outside the builder:
27
+
22
28
  ```ts
23
- import { getEtch, isEtchAvailable, EtchApiError } from '@digital-gravy/etch-public-api';
29
+ import { getEtch, isEtchAvailable } from "@digital-gravy/etch-public-api";
24
30
 
25
- if (!isEtchAvailable()) {
26
- // Not running inside the Etch builder (or it hasn't finished loading).
27
- return;
31
+ function run() {
32
+ if (!isEtchAvailable()) return; // not running inside the Etch builder
33
+ const etch = getEtch();
34
+ const textIds = etch.blocks.find({ type: "text" });
35
+ etch.blocks.setText(textIds[0], "Hello world");
28
36
  }
37
+ ```
29
38
 
30
- const etch = getEtch();
39
+ `getEtch()` throws an `EtchApiError` with code `NOT_AVAILABLE` when the builder
40
+ isn't on the page, so you can also `try`/`catch` it. If your script may run
41
+ before the builder has finished loading, wait until it appears:
42
+
43
+ ```ts
44
+ import { getEtch, isEtchAvailable } from "@digital-gravy/etch-public-api";
45
+
46
+ async function whenEtchReady(timeoutMs = 10_000) {
47
+ const start = Date.now();
48
+ while (!isEtchAvailable()) {
49
+ if (Date.now() - start > timeoutMs) throw new Error("Etch did not load");
50
+ await new Promise((resolve) => setTimeout(resolve, 100));
51
+ }
52
+ return getEtch();
53
+ }
31
54
 
55
+ const etch = await whenEtchReady();
56
+ ```
57
+
58
+ ### Reading and mutating
59
+
60
+ ```ts
32
61
  // Read
33
- const textIds = etch.blocks.find({ type: 'text' });
62
+ const textIds = etch.blocks.find({ type: "text" });
34
63
  const json = etch.blocks.getJson(textIds[0]);
35
64
 
36
65
  // Mutate (routes through the same guarded paths as the UI; undo/redo works)
37
- etch.blocks.setText(textIds[0], 'Hello world');
38
- etch.blocks.addClass(textIds[0], 'lead');
66
+ etch.blocks.setText(textIds[0], "Hello world");
67
+ etch.blocks.addClass(textIds[0], "lead");
39
68
 
40
- const styleId = etch.styles.create('.lead', 'font-size: 1.25rem;');
41
- etch.styles.setVariable('--brand', '#0af');
69
+ const styleId = etch.styles.create(".lead", "font-size: 1.25rem;");
70
+ etch.styles.setVariable("--brand", "#0af");
42
71
 
43
72
  // Persist (blocks/styles wait for save; stylesheets/components/fields persist immediately)
44
73
  await etch.saveAsync();
@@ -50,26 +79,71 @@ Methods throw a typed `EtchApiError` with a `code`, rather than returning
50
79
  sentinels:
51
80
 
52
81
  ```ts
53
- import { isEtchApiError } from '@digital-gravy/etch-public-api';
82
+ import { isEtchApiError } from "@digital-gravy/etch-public-api";
54
83
 
55
84
  try {
56
- etch.blocks.getJson('does-not-exist');
85
+ etch.blocks.getJson("does-not-exist");
57
86
  } catch (err) {
58
- if (isEtchApiError(err)) {
59
- console.warn(err.code, err.message); // e.g. "BLOCK_NOT_FOUND"
60
- }
87
+ if (isEtchApiError(err)) {
88
+ console.warn(err.code, err.message); // e.g. "BLOCK_NOT_FOUND"
89
+ }
61
90
  }
62
91
  ```
63
92
 
64
93
  ### Version negotiation
65
94
 
66
- `getEtch()` accepts the contract version your plugin targets. On today's `0.x`
67
- runtime this is a best-effort check (a console warning on mismatch). On a future
68
- stable runtime that exposes a native `connect()`, the call is delegated to it
69
- and you receive a version-pinned instance:
95
+ **Today (`0.x`): there is no real negotiation yet.** The runtime reports
96
+ `etch.apiVersion` as the coarse marker `'0.x'` (not a precise version), and
97
+ `getEtch({ apiVersion })` only does a best-effort major-version check that
98
+ `console.warn`s on mismatch — it never throws or adapts. While the surface is
99
+ experimental, **prefer feature detection** over version comparison:
100
+
101
+ ```ts
102
+ const etch = getEtch();
103
+ if (typeof etch.blocks.someNewMethod === "function") {
104
+ // safe to use
105
+ }
106
+ ```
107
+
108
+ **In the future (once the contract reaches `1.x`):** `etch.apiVersion` will
109
+ report a real semver, and `getEtch()` will negotiate against the runtime's
110
+ native `connect()` — returning an instance pinned to the version your plugin
111
+ targets, and failing fast when the runtime can't satisfy it:
70
112
 
71
113
  ```ts
72
- const etch = getEtch({ apiVersion: '^1.0', id: 'my-plugin' });
114
+ // Reserved API — shape of versioned access once the contract is stable:
115
+ const etch = getEtch({ apiVersion: "^1.0", id: "my-plugin" });
116
+ // └─ delegates to window.etch.connect({ apiVersion: "^1.0", id }) when present,
117
+ // yielding a version-pinned instance (throws on an incompatible runtime).
118
+ ```
119
+
120
+ `getEtch()` already accepts `{ apiVersion, id }` today, so plugins can pass them
121
+ now and have them take effect automatically once the stable runtime ships — no
122
+ code change needed.
123
+
124
+ ### Typed block JSON
125
+
126
+ Block JSON is a **discriminated union** on `type`, so the compiler flags a block
127
+ whose payload doesn't match its declared type, and `getJson()` / `getTree()`
128
+ results narrow by `type`:
129
+
130
+ ```ts
131
+ const block = etch.blocks.getJson(id);
132
+
133
+ if (block.type === "etch/text") {
134
+ console.log(block.text); // narrowed to the text-block shape
135
+ } else if (block.type === "etch/element") {
136
+ console.log(block.tag, block.attributes);
137
+ }
138
+
139
+ // Authoring is checked too — this is a type error (an `etch/text` has no `tag`):
140
+ etch.blocks.create({
141
+ type: "etch/text",
142
+ version: 1,
143
+ context: {},
144
+ children: [],
145
+ tag: "div",
146
+ });
73
147
  ```
74
148
 
75
149
  ### Types only
@@ -78,7 +152,10 @@ Every contract type is exported and dependency-free, so you can use them
78
152
  directly:
79
153
 
80
154
  ```ts
81
- import type { PublicBlockJson, EtchBlocksApi } from '@digital-gravy/etch-public-api';
155
+ import type {
156
+ PublicBlockJson,
157
+ EtchBlocksApi,
158
+ } from "@digital-gravy/etch-public-api";
82
159
  ```
83
160
 
84
161
  You can also work against the global directly — the package augments
package/dist/index.d.cts CHANGED
@@ -81,42 +81,53 @@ interface EtchTextBlockJson extends EtchBlockCommon {
81
81
  /** The block's text content. */
82
82
  text: string;
83
83
  }
84
- /** A static HTML element block (`etch/element`) with a concrete tag. */
84
+ /**
85
+ * A static HTML element block (`etch/element`) with a concrete tag.
86
+ *
87
+ * The global `styles` applied to the block are **read-only**: they appear on the
88
+ * read shape ({@link PublicBlockJson}) but are not settable here, so
89
+ * `blocks.create()` / `blocks.replace()` ignore them.
90
+ */
85
91
  interface EtchElementBlockJson extends EtchBlockCommon {
86
92
  type: "etch/element";
87
93
  /** The HTML tag name, e.g. `div`, `p`, `h1`. */
88
94
  tag: string;
89
95
  /** HTML attributes. */
90
96
  attributes: EtchHtmlAttributes;
91
- /** Ids of the global styles applied to this block. */
92
- styles: string[];
93
97
  }
94
98
  /**
95
99
  * A dynamic element block (`etch/dynamic-element`). Its rendered tag is read
96
100
  * from `attributes.tag` rather than a dedicated field.
101
+ *
102
+ * The global `styles` applied to the block are **read-only** (see
103
+ * {@link EtchElementBlockJson}).
97
104
  */
98
105
  interface EtchDynamicElementBlockJson extends EtchBlockCommon {
99
106
  type: "etch/dynamic-element";
100
107
  /** HTML attributes (the rendered tag is read from `attributes.tag`). */
101
108
  attributes: EtchHtmlAttributes;
102
- /** Ids of the global styles applied to this block. */
103
- styles: string[];
104
109
  }
105
- /** A dynamic image block (`etch/dynamic-image`), rendered as an `<img>`. */
110
+ /**
111
+ * A dynamic image block (`etch/dynamic-image`), rendered as an `<img>`.
112
+ *
113
+ * The global `styles` applied to the block are **read-only** (see
114
+ * {@link EtchElementBlockJson}).
115
+ */
106
116
  interface EtchDynamicImageBlockJson extends EtchBlockCommon {
107
117
  type: "etch/dynamic-image";
108
118
  /** HTML attributes (e.g. `src`, `alt`). */
109
119
  attributes: EtchHtmlAttributes;
110
- /** Ids of the global styles applied to this block. */
111
- styles: string[];
112
120
  }
113
- /** An inline SVG block (`etch/svg`). */
121
+ /**
122
+ * An inline SVG block (`etch/svg`).
123
+ *
124
+ * The global `styles` applied to the block are **read-only** (see
125
+ * {@link EtchElementBlockJson}).
126
+ */
114
127
  interface EtchSvgBlockJson extends EtchBlockCommon {
115
128
  type: "etch/svg";
116
129
  /** HTML/SVG attributes. */
117
130
  attributes: EtchHtmlAttributes;
118
- /** Ids of the global styles applied to this block. */
119
- styles: string[];
120
131
  }
121
132
  /** A loop block (`etch/loop`) that repeats its children over a data source. */
122
133
  interface EtchLoopBlockJson extends EtchBlockCommon {
@@ -196,8 +207,25 @@ interface BlockIdentity {
196
207
  /** Child blocks, each itself a `PublicBlockJson`. */
197
208
  children: PublicBlockJson[];
198
209
  }
199
- /** Attach read-only identity (`id`/`parentId`) to a block, recursively. */
200
- type WithIdentity<B> = B extends EtchBlockCommon ? Omit<B, "children"> & BlockIdentity : never;
210
+ /**
211
+ * Block types that carry global `styles` (ids of the applied global styles).
212
+ * These are **read-only**: exposed when reading a block, but not part of the
213
+ * writable {@link EtchBlockJson}, so they cannot be set via
214
+ * `blocks.create()` / `blocks.replace()`.
215
+ */
216
+ type StyledBlockType = "etch/element" | "etch/dynamic-element" | "etch/dynamic-image" | "etch/svg";
217
+ /** The read-only `styles` exposed on a {@link StyledBlockType} when reading. */
218
+ interface ReadOnlyBlockStyles {
219
+ /** Ids of the global styles applied to this block (read-only). */
220
+ readonly styles: string[];
221
+ }
222
+ /**
223
+ * Attach read-only identity (`id`/`parentId`) to a block, recursively, plus the
224
+ * read-only `styles` array for the {@link StyledBlockType}s that carry one.
225
+ */
226
+ type WithIdentity<B> = B extends EtchBlockCommon ? Omit<B, "children"> & BlockIdentity & (B extends {
227
+ type: StyledBlockType;
228
+ } ? ReadOnlyBlockStyles : unknown) : never;
201
229
  /**
202
230
  * A block serialized for reading: an {@link EtchBlockJson} variant plus its
203
231
  * `id` and `parentId` (`null` at the document root), attached recursively so
@@ -562,7 +590,36 @@ interface ComponentPropertyBase {
562
590
  /** Optional human-readable description. */
563
591
  description?: string;
564
592
  }
565
- /** A string property, optionally specialized (color picker, image, select, …). */
593
+ /**
594
+ * Options for a `select` string property: newline-separated entries, each
595
+ * `Label : Value` (note the spaces around the colon). A line with no ` : ` uses
596
+ * its text as both label and value, and the **first line is the default**.
597
+ *
598
+ * It is a plain `string` (the format is a runtime convention, not enforced by
599
+ * the type) — parse it by splitting on `\n`, then each line on `" : "`.
600
+ *
601
+ * @example
602
+ * ```text
603
+ * Red : #ff0000
604
+ * Green : #00ff00
605
+ * Blue : #0000ff
606
+ * ```
607
+ */
608
+ type SelectOptionsString = string;
609
+ /**
610
+ * A string property.
611
+ *
612
+ * `type.specialized` selects a string sub-type; a plain text property omits it.
613
+ * Values the builder actually uses:
614
+ * - `'image'` — image URL
615
+ * - `'wpMediaId'` — WordPress media id
616
+ * - `'select'` — a choice from `options`
617
+ * - `'array'` — a loop/array binding
618
+ *
619
+ * `'color'` and `'url'` are defined in the runtime enum but are **not currently
620
+ * used** by the builder — do not rely on them. (A `'condition'` string is a
621
+ * gated group, modelled separately as {@link ConditionComponentProperty}.)
622
+ */
566
623
  interface StringComponentProperty {
567
624
  type: {
568
625
  primitive: "string";
@@ -572,6 +629,12 @@ interface StringComponentProperty {
572
629
  default?: string;
573
630
  /** Allowed values when `specialized` is `select`. */
574
631
  options?: string[];
632
+ /**
633
+ * Options for a `select` property, as a newline-separated string. Each line
634
+ * is `Label : Value` (note the spaces around the colon); a line with no ` : `
635
+ * uses its text as both label and value. The **first line is the default**.
636
+ */
637
+ selectOptionsString?: SelectOptionsString;
575
638
  }
576
639
  /** A numeric property. */
577
640
  interface NumberComponentProperty {
@@ -591,7 +654,13 @@ interface BooleanComponentProperty {
591
654
  /** Default value (a string is allowed for expression-driven defaults). */
592
655
  default?: boolean | string;
593
656
  }
594
- /** An object property carrying structured data. */
657
+ /**
658
+ * A generic object property carrying structured data.
659
+ *
660
+ * `type.specialized` is left open (`string`) because the runtime does not
661
+ * constrain it to an enum. The one reserved value is `'group'`, modelled as
662
+ * {@link GroupComponentProperty}; a plain object omits `specialized`.
663
+ */
595
664
  interface ObjectComponentProperty {
596
665
  type: {
597
666
  primitive: "object";
@@ -600,7 +669,13 @@ interface ObjectComponentProperty {
600
669
  /** Default value. */
601
670
  default?: Record<string, unknown> | unknown[];
602
671
  }
603
- /** An array property carrying a list of values. */
672
+ /**
673
+ * A generic array property carrying a list of values.
674
+ *
675
+ * `type.specialized` is left open (`string`). The reserved values are `'class'`
676
+ * ({@link ClassComponentProperty}) and `'repeater'`
677
+ * ({@link RepeaterComponentProperty}); a plain array omits `specialized`.
678
+ */
604
679
  interface ArrayComponentProperty {
605
680
  type: {
606
681
  primitive: "array";
@@ -609,7 +684,7 @@ interface ArrayComponentProperty {
609
684
  /** Default value. */
610
685
  default?: unknown[];
611
686
  }
612
- /** A property holding a list of CSS class names. */
687
+ /** A list of CSS class names — an `array` specialized as `'class'`. */
613
688
  interface ClassComponentProperty {
614
689
  type: {
615
690
  primitive: "array";
@@ -618,7 +693,7 @@ interface ClassComponentProperty {
618
693
  /** Default value. */
619
694
  default?: string[];
620
695
  }
621
- /** A group of nested properties (no default of its own). */
696
+ /** A group of nested properties — an `object` specialized as `'group'` (no default). */
622
697
  interface GroupComponentProperty {
623
698
  type: {
624
699
  primitive: "object";
@@ -627,7 +702,7 @@ interface GroupComponentProperty {
627
702
  /** The nested properties in this group. */
628
703
  properties: ComponentProperty[];
629
704
  }
630
- /** A repeatable group of nested properties (no default of its own). */
705
+ /** A repeatable group — an `array` specialized as `'repeater'` (no default). */
631
706
  interface RepeaterComponentProperty {
632
707
  type: {
633
708
  primitive: "array";
@@ -636,7 +711,7 @@ interface RepeaterComponentProperty {
636
711
  /** The nested properties repeated per row. */
637
712
  properties: ComponentProperty[];
638
713
  }
639
- /** A conditional group gated by an expression. */
714
+ /** A conditional group gated by an expression — a `string` specialized as `'condition'`. */
640
715
  interface ConditionComponentProperty {
641
716
  type: {
642
717
  primitive: "string";
@@ -647,7 +722,35 @@ interface ConditionComponentProperty {
647
722
  /** The condition expression. */
648
723
  default?: string;
649
724
  }
650
- /** A single configurable property of a component. */
725
+ /**
726
+ * A single configurable property of a component.
727
+ *
728
+ * The `type` object carries a `primitive` plus an optional `specialized`
729
+ * refinement. The combinations the builder actually uses:
730
+ *
731
+ * - `string` — plain text; or `'image'` | `'wpMediaId'` | `'select'` |
732
+ * `'array'`; or `'condition'` (a gated group with nested `properties`)
733
+ * - `number` — numeric
734
+ * - `boolean` — boolean
735
+ * - `object` — generic object; or `'group'` (nested `properties`)
736
+ * - `array` — generic array; or `'class'` (CSS classes); or `'repeater'`
737
+ * (repeating group with nested `properties`)
738
+ *
739
+ * (`'color'`/`'url'` are defined in the string enum but unused — see
740
+ * {@link StringComponentProperty}.)
741
+ *
742
+ * `object`/`array` `specialized` is typed as an open `string` because the
743
+ * runtime does not constrain it to an enum. Because plain `string`/`object`/
744
+ * `array` variants omit `specialized`, narrow before reading it:
745
+ *
746
+ * ```ts
747
+ * if (prop.type.primitive === "object" && "specialized" in prop.type) {
748
+ * if (prop.type.specialized === "group") {
749
+ * // …group property
750
+ * }
751
+ * }
752
+ * ```
753
+ */
651
754
  type ComponentProperty = ComponentPropertyBase & (StringComponentProperty | NumberComponentProperty | BooleanComponentProperty | ObjectComponentProperty | ArrayComponentProperty | ClassComponentProperty | GroupComponentProperty | RepeaterComponentProperty | ConditionComponentProperty);
652
755
  /** Component metadata without its block tree (returned by `components.list()`). */
653
756
  interface PublicComponentSummary {
@@ -1017,4 +1120,4 @@ declare function isEtchApiError(value: unknown): value is EtchApiError;
1017
1120
  */
1018
1121
  declare const ETCH_API_VERSION = "0.x";
1019
1122
 
1020
- export { type ArrayComponentProperty, type BlockLoopBinding, type BlockPatch, type BooleanComponentProperty, type BooleanParam, type ClassComponentProperty, type ColorScheme, type ComponentPatch, type ComponentProperty, type ComponentPropertyBase, type ConditionComponentProperty, type ConnectOptions, type CustomField, type CustomFieldAssignment, type CustomFieldGroup, type CustomFieldType, ETCH_API_VERSION, type Etch, EtchApiError, type EtchApiErrorCode, type EtchBlockCommon, type EtchBlockContext, type EtchBlockJson, type EtchBlockOptions, type EtchBlockScript, type EtchBlockType, type EtchBlockTypeName, type EtchBlocksApi, type EtchComponentBlockJson, type EtchComponentsApi, type EtchConditionBlockJson, type EtchDynamicElementBlockJson, type EtchDynamicImageBlockJson, type EtchElementBlockJson, type EtchFieldsApi, type EtchHistoryApi, type EtchHtmlAttributes, type EtchLoop, type EtchLoopBlockJson, type EtchLoopConfig, type EtchLoopObj, type EtchLoopsApi, type EtchNavigationApi, type EtchPassthroughBlockJson, type EtchPostContentBlockJson, type EtchRawHtmlBlockJson, type EtchSlotContentBlockJson, type EtchSlotPlaceholderBlockJson, type EtchStylesApi, type EtchStylesheetsApi, type EtchSvgBlockJson, type EtchTextBlockJson, type EtchUiApi, type FindBlocksPredicate, type GroupComponentProperty, type GutenbergBlock, type LoopParamRef, type MetaQueryItem, type NavigationPlace, type NumberComponentProperty, type NumericParam, type ObjectComponentProperty, type PostCustomFieldGroupEntry, type PostCustomFieldValueEntry, type PostCustomFieldValueResponse, type PostCustomFieldValuesResponse, type PostSummary, type PublicBlockJson, type PublicComponentJson, type PublicComponentSummary, type RepeaterComponentProperty, type ResolvedCustomField, type StringComponentProperty, type StylePatch, type StylesheetInput, type StylesheetPatch, type StylesheetSummary, type StylesheetType, type TaxQueryItem, type TemplateSummary, type WpQueryArgs, type WpTermsArgs, type WpUsersArgs, getEtch, isEtchApiError, isEtchAvailable };
1123
+ export { type ArrayComponentProperty, type BlockLoopBinding, type BlockPatch, type BooleanComponentProperty, type BooleanParam, type ClassComponentProperty, type ColorScheme, type ComponentPatch, type ComponentProperty, type ComponentPropertyBase, type ConditionComponentProperty, type ConnectOptions, type CustomField, type CustomFieldAssignment, type CustomFieldGroup, type CustomFieldType, ETCH_API_VERSION, type Etch, EtchApiError, type EtchApiErrorCode, type EtchBlockCommon, type EtchBlockContext, type EtchBlockJson, type EtchBlockOptions, type EtchBlockScript, type EtchBlockType, type EtchBlockTypeName, type EtchBlocksApi, type EtchComponentBlockJson, type EtchComponentsApi, type EtchConditionBlockJson, type EtchDynamicElementBlockJson, type EtchDynamicImageBlockJson, type EtchElementBlockJson, type EtchFieldsApi, type EtchHistoryApi, type EtchHtmlAttributes, type EtchLoop, type EtchLoopBlockJson, type EtchLoopConfig, type EtchLoopObj, type EtchLoopsApi, type EtchNavigationApi, type EtchPassthroughBlockJson, type EtchPostContentBlockJson, type EtchRawHtmlBlockJson, type EtchSlotContentBlockJson, type EtchSlotPlaceholderBlockJson, type EtchStylesApi, type EtchStylesheetsApi, type EtchSvgBlockJson, type EtchTextBlockJson, type EtchUiApi, type FindBlocksPredicate, type GroupComponentProperty, type GutenbergBlock, type LoopParamRef, type MetaQueryItem, type NavigationPlace, type NumberComponentProperty, type NumericParam, type ObjectComponentProperty, type PostCustomFieldGroupEntry, type PostCustomFieldValueEntry, type PostCustomFieldValueResponse, type PostCustomFieldValuesResponse, type PostSummary, type PublicBlockJson, type PublicComponentJson, type PublicComponentSummary, type RepeaterComponentProperty, type ResolvedCustomField, type SelectOptionsString, type StringComponentProperty, type StylePatch, type StylesheetInput, type StylesheetPatch, type StylesheetSummary, type StylesheetType, type TaxQueryItem, type TemplateSummary, type WpQueryArgs, type WpTermsArgs, type WpUsersArgs, getEtch, isEtchApiError, isEtchAvailable };
package/dist/index.d.ts CHANGED
@@ -81,42 +81,53 @@ interface EtchTextBlockJson extends EtchBlockCommon {
81
81
  /** The block's text content. */
82
82
  text: string;
83
83
  }
84
- /** A static HTML element block (`etch/element`) with a concrete tag. */
84
+ /**
85
+ * A static HTML element block (`etch/element`) with a concrete tag.
86
+ *
87
+ * The global `styles` applied to the block are **read-only**: they appear on the
88
+ * read shape ({@link PublicBlockJson}) but are not settable here, so
89
+ * `blocks.create()` / `blocks.replace()` ignore them.
90
+ */
85
91
  interface EtchElementBlockJson extends EtchBlockCommon {
86
92
  type: "etch/element";
87
93
  /** The HTML tag name, e.g. `div`, `p`, `h1`. */
88
94
  tag: string;
89
95
  /** HTML attributes. */
90
96
  attributes: EtchHtmlAttributes;
91
- /** Ids of the global styles applied to this block. */
92
- styles: string[];
93
97
  }
94
98
  /**
95
99
  * A dynamic element block (`etch/dynamic-element`). Its rendered tag is read
96
100
  * from `attributes.tag` rather than a dedicated field.
101
+ *
102
+ * The global `styles` applied to the block are **read-only** (see
103
+ * {@link EtchElementBlockJson}).
97
104
  */
98
105
  interface EtchDynamicElementBlockJson extends EtchBlockCommon {
99
106
  type: "etch/dynamic-element";
100
107
  /** HTML attributes (the rendered tag is read from `attributes.tag`). */
101
108
  attributes: EtchHtmlAttributes;
102
- /** Ids of the global styles applied to this block. */
103
- styles: string[];
104
109
  }
105
- /** A dynamic image block (`etch/dynamic-image`), rendered as an `<img>`. */
110
+ /**
111
+ * A dynamic image block (`etch/dynamic-image`), rendered as an `<img>`.
112
+ *
113
+ * The global `styles` applied to the block are **read-only** (see
114
+ * {@link EtchElementBlockJson}).
115
+ */
106
116
  interface EtchDynamicImageBlockJson extends EtchBlockCommon {
107
117
  type: "etch/dynamic-image";
108
118
  /** HTML attributes (e.g. `src`, `alt`). */
109
119
  attributes: EtchHtmlAttributes;
110
- /** Ids of the global styles applied to this block. */
111
- styles: string[];
112
120
  }
113
- /** An inline SVG block (`etch/svg`). */
121
+ /**
122
+ * An inline SVG block (`etch/svg`).
123
+ *
124
+ * The global `styles` applied to the block are **read-only** (see
125
+ * {@link EtchElementBlockJson}).
126
+ */
114
127
  interface EtchSvgBlockJson extends EtchBlockCommon {
115
128
  type: "etch/svg";
116
129
  /** HTML/SVG attributes. */
117
130
  attributes: EtchHtmlAttributes;
118
- /** Ids of the global styles applied to this block. */
119
- styles: string[];
120
131
  }
121
132
  /** A loop block (`etch/loop`) that repeats its children over a data source. */
122
133
  interface EtchLoopBlockJson extends EtchBlockCommon {
@@ -196,8 +207,25 @@ interface BlockIdentity {
196
207
  /** Child blocks, each itself a `PublicBlockJson`. */
197
208
  children: PublicBlockJson[];
198
209
  }
199
- /** Attach read-only identity (`id`/`parentId`) to a block, recursively. */
200
- type WithIdentity<B> = B extends EtchBlockCommon ? Omit<B, "children"> & BlockIdentity : never;
210
+ /**
211
+ * Block types that carry global `styles` (ids of the applied global styles).
212
+ * These are **read-only**: exposed when reading a block, but not part of the
213
+ * writable {@link EtchBlockJson}, so they cannot be set via
214
+ * `blocks.create()` / `blocks.replace()`.
215
+ */
216
+ type StyledBlockType = "etch/element" | "etch/dynamic-element" | "etch/dynamic-image" | "etch/svg";
217
+ /** The read-only `styles` exposed on a {@link StyledBlockType} when reading. */
218
+ interface ReadOnlyBlockStyles {
219
+ /** Ids of the global styles applied to this block (read-only). */
220
+ readonly styles: string[];
221
+ }
222
+ /**
223
+ * Attach read-only identity (`id`/`parentId`) to a block, recursively, plus the
224
+ * read-only `styles` array for the {@link StyledBlockType}s that carry one.
225
+ */
226
+ type WithIdentity<B> = B extends EtchBlockCommon ? Omit<B, "children"> & BlockIdentity & (B extends {
227
+ type: StyledBlockType;
228
+ } ? ReadOnlyBlockStyles : unknown) : never;
201
229
  /**
202
230
  * A block serialized for reading: an {@link EtchBlockJson} variant plus its
203
231
  * `id` and `parentId` (`null` at the document root), attached recursively so
@@ -562,7 +590,36 @@ interface ComponentPropertyBase {
562
590
  /** Optional human-readable description. */
563
591
  description?: string;
564
592
  }
565
- /** A string property, optionally specialized (color picker, image, select, …). */
593
+ /**
594
+ * Options for a `select` string property: newline-separated entries, each
595
+ * `Label : Value` (note the spaces around the colon). A line with no ` : ` uses
596
+ * its text as both label and value, and the **first line is the default**.
597
+ *
598
+ * It is a plain `string` (the format is a runtime convention, not enforced by
599
+ * the type) — parse it by splitting on `\n`, then each line on `" : "`.
600
+ *
601
+ * @example
602
+ * ```text
603
+ * Red : #ff0000
604
+ * Green : #00ff00
605
+ * Blue : #0000ff
606
+ * ```
607
+ */
608
+ type SelectOptionsString = string;
609
+ /**
610
+ * A string property.
611
+ *
612
+ * `type.specialized` selects a string sub-type; a plain text property omits it.
613
+ * Values the builder actually uses:
614
+ * - `'image'` — image URL
615
+ * - `'wpMediaId'` — WordPress media id
616
+ * - `'select'` — a choice from `options`
617
+ * - `'array'` — a loop/array binding
618
+ *
619
+ * `'color'` and `'url'` are defined in the runtime enum but are **not currently
620
+ * used** by the builder — do not rely on them. (A `'condition'` string is a
621
+ * gated group, modelled separately as {@link ConditionComponentProperty}.)
622
+ */
566
623
  interface StringComponentProperty {
567
624
  type: {
568
625
  primitive: "string";
@@ -572,6 +629,12 @@ interface StringComponentProperty {
572
629
  default?: string;
573
630
  /** Allowed values when `specialized` is `select`. */
574
631
  options?: string[];
632
+ /**
633
+ * Options for a `select` property, as a newline-separated string. Each line
634
+ * is `Label : Value` (note the spaces around the colon); a line with no ` : `
635
+ * uses its text as both label and value. The **first line is the default**.
636
+ */
637
+ selectOptionsString?: SelectOptionsString;
575
638
  }
576
639
  /** A numeric property. */
577
640
  interface NumberComponentProperty {
@@ -591,7 +654,13 @@ interface BooleanComponentProperty {
591
654
  /** Default value (a string is allowed for expression-driven defaults). */
592
655
  default?: boolean | string;
593
656
  }
594
- /** An object property carrying structured data. */
657
+ /**
658
+ * A generic object property carrying structured data.
659
+ *
660
+ * `type.specialized` is left open (`string`) because the runtime does not
661
+ * constrain it to an enum. The one reserved value is `'group'`, modelled as
662
+ * {@link GroupComponentProperty}; a plain object omits `specialized`.
663
+ */
595
664
  interface ObjectComponentProperty {
596
665
  type: {
597
666
  primitive: "object";
@@ -600,7 +669,13 @@ interface ObjectComponentProperty {
600
669
  /** Default value. */
601
670
  default?: Record<string, unknown> | unknown[];
602
671
  }
603
- /** An array property carrying a list of values. */
672
+ /**
673
+ * A generic array property carrying a list of values.
674
+ *
675
+ * `type.specialized` is left open (`string`). The reserved values are `'class'`
676
+ * ({@link ClassComponentProperty}) and `'repeater'`
677
+ * ({@link RepeaterComponentProperty}); a plain array omits `specialized`.
678
+ */
604
679
  interface ArrayComponentProperty {
605
680
  type: {
606
681
  primitive: "array";
@@ -609,7 +684,7 @@ interface ArrayComponentProperty {
609
684
  /** Default value. */
610
685
  default?: unknown[];
611
686
  }
612
- /** A property holding a list of CSS class names. */
687
+ /** A list of CSS class names — an `array` specialized as `'class'`. */
613
688
  interface ClassComponentProperty {
614
689
  type: {
615
690
  primitive: "array";
@@ -618,7 +693,7 @@ interface ClassComponentProperty {
618
693
  /** Default value. */
619
694
  default?: string[];
620
695
  }
621
- /** A group of nested properties (no default of its own). */
696
+ /** A group of nested properties — an `object` specialized as `'group'` (no default). */
622
697
  interface GroupComponentProperty {
623
698
  type: {
624
699
  primitive: "object";
@@ -627,7 +702,7 @@ interface GroupComponentProperty {
627
702
  /** The nested properties in this group. */
628
703
  properties: ComponentProperty[];
629
704
  }
630
- /** A repeatable group of nested properties (no default of its own). */
705
+ /** A repeatable group — an `array` specialized as `'repeater'` (no default). */
631
706
  interface RepeaterComponentProperty {
632
707
  type: {
633
708
  primitive: "array";
@@ -636,7 +711,7 @@ interface RepeaterComponentProperty {
636
711
  /** The nested properties repeated per row. */
637
712
  properties: ComponentProperty[];
638
713
  }
639
- /** A conditional group gated by an expression. */
714
+ /** A conditional group gated by an expression — a `string` specialized as `'condition'`. */
640
715
  interface ConditionComponentProperty {
641
716
  type: {
642
717
  primitive: "string";
@@ -647,7 +722,35 @@ interface ConditionComponentProperty {
647
722
  /** The condition expression. */
648
723
  default?: string;
649
724
  }
650
- /** A single configurable property of a component. */
725
+ /**
726
+ * A single configurable property of a component.
727
+ *
728
+ * The `type` object carries a `primitive` plus an optional `specialized`
729
+ * refinement. The combinations the builder actually uses:
730
+ *
731
+ * - `string` — plain text; or `'image'` | `'wpMediaId'` | `'select'` |
732
+ * `'array'`; or `'condition'` (a gated group with nested `properties`)
733
+ * - `number` — numeric
734
+ * - `boolean` — boolean
735
+ * - `object` — generic object; or `'group'` (nested `properties`)
736
+ * - `array` — generic array; or `'class'` (CSS classes); or `'repeater'`
737
+ * (repeating group with nested `properties`)
738
+ *
739
+ * (`'color'`/`'url'` are defined in the string enum but unused — see
740
+ * {@link StringComponentProperty}.)
741
+ *
742
+ * `object`/`array` `specialized` is typed as an open `string` because the
743
+ * runtime does not constrain it to an enum. Because plain `string`/`object`/
744
+ * `array` variants omit `specialized`, narrow before reading it:
745
+ *
746
+ * ```ts
747
+ * if (prop.type.primitive === "object" && "specialized" in prop.type) {
748
+ * if (prop.type.specialized === "group") {
749
+ * // …group property
750
+ * }
751
+ * }
752
+ * ```
753
+ */
651
754
  type ComponentProperty = ComponentPropertyBase & (StringComponentProperty | NumberComponentProperty | BooleanComponentProperty | ObjectComponentProperty | ArrayComponentProperty | ClassComponentProperty | GroupComponentProperty | RepeaterComponentProperty | ConditionComponentProperty);
652
755
  /** Component metadata without its block tree (returned by `components.list()`). */
653
756
  interface PublicComponentSummary {
@@ -1017,4 +1120,4 @@ declare function isEtchApiError(value: unknown): value is EtchApiError;
1017
1120
  */
1018
1121
  declare const ETCH_API_VERSION = "0.x";
1019
1122
 
1020
- export { type ArrayComponentProperty, type BlockLoopBinding, type BlockPatch, type BooleanComponentProperty, type BooleanParam, type ClassComponentProperty, type ColorScheme, type ComponentPatch, type ComponentProperty, type ComponentPropertyBase, type ConditionComponentProperty, type ConnectOptions, type CustomField, type CustomFieldAssignment, type CustomFieldGroup, type CustomFieldType, ETCH_API_VERSION, type Etch, EtchApiError, type EtchApiErrorCode, type EtchBlockCommon, type EtchBlockContext, type EtchBlockJson, type EtchBlockOptions, type EtchBlockScript, type EtchBlockType, type EtchBlockTypeName, type EtchBlocksApi, type EtchComponentBlockJson, type EtchComponentsApi, type EtchConditionBlockJson, type EtchDynamicElementBlockJson, type EtchDynamicImageBlockJson, type EtchElementBlockJson, type EtchFieldsApi, type EtchHistoryApi, type EtchHtmlAttributes, type EtchLoop, type EtchLoopBlockJson, type EtchLoopConfig, type EtchLoopObj, type EtchLoopsApi, type EtchNavigationApi, type EtchPassthroughBlockJson, type EtchPostContentBlockJson, type EtchRawHtmlBlockJson, type EtchSlotContentBlockJson, type EtchSlotPlaceholderBlockJson, type EtchStylesApi, type EtchStylesheetsApi, type EtchSvgBlockJson, type EtchTextBlockJson, type EtchUiApi, type FindBlocksPredicate, type GroupComponentProperty, type GutenbergBlock, type LoopParamRef, type MetaQueryItem, type NavigationPlace, type NumberComponentProperty, type NumericParam, type ObjectComponentProperty, type PostCustomFieldGroupEntry, type PostCustomFieldValueEntry, type PostCustomFieldValueResponse, type PostCustomFieldValuesResponse, type PostSummary, type PublicBlockJson, type PublicComponentJson, type PublicComponentSummary, type RepeaterComponentProperty, type ResolvedCustomField, type StringComponentProperty, type StylePatch, type StylesheetInput, type StylesheetPatch, type StylesheetSummary, type StylesheetType, type TaxQueryItem, type TemplateSummary, type WpQueryArgs, type WpTermsArgs, type WpUsersArgs, getEtch, isEtchApiError, isEtchAvailable };
1123
+ export { type ArrayComponentProperty, type BlockLoopBinding, type BlockPatch, type BooleanComponentProperty, type BooleanParam, type ClassComponentProperty, type ColorScheme, type ComponentPatch, type ComponentProperty, type ComponentPropertyBase, type ConditionComponentProperty, type ConnectOptions, type CustomField, type CustomFieldAssignment, type CustomFieldGroup, type CustomFieldType, ETCH_API_VERSION, type Etch, EtchApiError, type EtchApiErrorCode, type EtchBlockCommon, type EtchBlockContext, type EtchBlockJson, type EtchBlockOptions, type EtchBlockScript, type EtchBlockType, type EtchBlockTypeName, type EtchBlocksApi, type EtchComponentBlockJson, type EtchComponentsApi, type EtchConditionBlockJson, type EtchDynamicElementBlockJson, type EtchDynamicImageBlockJson, type EtchElementBlockJson, type EtchFieldsApi, type EtchHistoryApi, type EtchHtmlAttributes, type EtchLoop, type EtchLoopBlockJson, type EtchLoopConfig, type EtchLoopObj, type EtchLoopsApi, type EtchNavigationApi, type EtchPassthroughBlockJson, type EtchPostContentBlockJson, type EtchRawHtmlBlockJson, type EtchSlotContentBlockJson, type EtchSlotPlaceholderBlockJson, type EtchStylesApi, type EtchStylesheetsApi, type EtchSvgBlockJson, type EtchTextBlockJson, type EtchUiApi, type FindBlocksPredicate, type GroupComponentProperty, type GutenbergBlock, type LoopParamRef, type MetaQueryItem, type NavigationPlace, type NumberComponentProperty, type NumericParam, type ObjectComponentProperty, type PostCustomFieldGroupEntry, type PostCustomFieldValueEntry, type PostCustomFieldValueResponse, type PostCustomFieldValuesResponse, type PostSummary, type PublicBlockJson, type PublicComponentJson, type PublicComponentSummary, type RepeaterComponentProperty, type ResolvedCustomField, type SelectOptionsString, type StringComponentProperty, type StylePatch, type StylesheetInput, type StylesheetPatch, type StylesheetSummary, type StylesheetType, type TaxQueryItem, type TemplateSummary, type WpQueryArgs, type WpTermsArgs, type WpUsersArgs, getEtch, isEtchApiError, isEtchAvailable };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digital-gravy/etch-public-api",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "MIT-licensed typed client and contract for the Etch builder scripting API (window.etch). Etch itself is a separate proprietary product governed by its own commercial terms.",
5
5
  "license": "MIT",
6
6
  "type": "module",