@contentrail/editor 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 +121 -2
- package/dist/api/client.d.ts +52 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +92 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/types.d.ts +72 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +2 -0
- package/dist/api/types.js.map +1 -0
- package/dist/fields/defaults.d.ts +11 -0
- package/dist/fields/defaults.d.ts.map +1 -0
- package/dist/fields/defaults.js +93 -0
- package/dist/fields/defaults.js.map +1 -0
- package/dist/fields/types.d.ts +62 -0
- package/dist/fields/types.d.ts.map +1 -0
- package/dist/fields/types.js +6 -0
- package/dist/fields/types.js.map +1 -0
- package/dist/form/FieldControl.d.ts +11 -0
- package/dist/form/FieldControl.d.ts.map +1 -0
- package/dist/form/FieldControl.js +72 -0
- package/dist/form/FieldControl.js.map +1 -0
- package/dist/form/SchemaForm.d.ts +14 -0
- package/dist/form/SchemaForm.d.ts.map +1 -0
- package/dist/form/SchemaForm.js +33 -0
- package/dist/form/SchemaForm.js.map +1 -0
- package/dist/import/tabular.d.ts +35 -0
- package/dist/import/tabular.d.ts.map +1 -0
- package/dist/import/tabular.js +191 -0
- package/dist/import/tabular.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/state/draft.d.ts +12 -0
- package/dist/state/draft.d.ts.map +1 -0
- package/dist/state/draft.js +32 -0
- package/dist/state/draft.js.map +1 -0
- package/dist/styles.css +322 -0
- package/dist/ui/EditorApp.d.ts +10 -0
- package/dist/ui/EditorApp.d.ts.map +1 -0
- package/dist/ui/EditorApp.js +352 -0
- package/dist/ui/EditorApp.js.map +1 -0
- package/package.json +60 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ContentRail contributors
|
|
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,122 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@contentrail/editor`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Optional browser editor for ContentRail collections. Site owners edit entries through schema-driven forms; publishing goes through the authenticated Worker API.
|
|
4
|
+
|
|
5
|
+
## Architecture (ADR 019)
|
|
6
|
+
|
|
7
|
+
| Layer | Responsibility |
|
|
8
|
+
| ---------------------------- | ------------------------------------------ |
|
|
9
|
+
| Explicit field metadata | Labels, widgets, order, help text |
|
|
10
|
+
| `SchemaForm` / `EditorApp` | Preact UI |
|
|
11
|
+
| `createContentRailApiClient` | HTTPS calls to `@contentrail/cloudflare` |
|
|
12
|
+
| Worker + publisher | Authn/authz, validation, publish integrity |
|
|
13
|
+
|
|
14
|
+
Does **not** depend on `@contentrail/publisher` or `@contentrail/cloudflare` at runtime. Never embed Git/R2 credentials.
|
|
15
|
+
|
|
16
|
+
## Requirements
|
|
17
|
+
|
|
18
|
+
- Peer: `preact@^10.26.0`
|
|
19
|
+
- Depends on `@contentrail/core` and `read-excel-file@9.3.14`
|
|
20
|
+
- Node.js **≥ 22** for packaging/tooling; browser target is evergreen ES2022
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
> **Not on the public npm registry yet.** See `docs/PUBLISHING.md`.
|
|
25
|
+
|
|
26
|
+
### Monorepo (current)
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
pnpm install
|
|
30
|
+
pnpm --filter @contentrail/editor build
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Intended after first public `0.1.0` release
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
pnpm add @contentrail/editor preact
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Import styles: `import "@contentrail/editor/styles.css"`.
|
|
40
|
+
|
|
41
|
+
## UI stack
|
|
42
|
+
|
|
43
|
+
- **Preact 10** (peer dependency)
|
|
44
|
+
- Plain CSS (`@contentrail/editor/styles.css`)
|
|
45
|
+
- Target: evergreen browsers with ES2022 modules
|
|
46
|
+
|
|
47
|
+
## Supported field widgets
|
|
48
|
+
|
|
49
|
+
| Widget | Notes |
|
|
50
|
+
| ------------- | -------------------------------------------- |
|
|
51
|
+
| `text` | Single-line string |
|
|
52
|
+
| `textarea` | Multiline string |
|
|
53
|
+
| `number` | Numeric input |
|
|
54
|
+
| `boolean` | Checkbox |
|
|
55
|
+
| `enum` | Select from `options` |
|
|
56
|
+
| `object` | Nested field group |
|
|
57
|
+
| `array` | List of item widgets |
|
|
58
|
+
| `unsupported` | Non-editable notice (never silent free text) |
|
|
59
|
+
|
|
60
|
+
Zod remains the server validation source of truth. Editor metadata does not introspect Zod internals.
|
|
61
|
+
|
|
62
|
+
## Minimal usage
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { render } from "preact";
|
|
66
|
+
import { createContentRailApiClient, EditorApp } from "@contentrail/editor";
|
|
67
|
+
import "@contentrail/editor/styles.css";
|
|
68
|
+
|
|
69
|
+
const api = createContentRailApiClient({
|
|
70
|
+
baseUrl: "https://your-worker.example",
|
|
71
|
+
credentials: "include", // Access session cookie
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
render(
|
|
75
|
+
<EditorApp
|
|
76
|
+
api={api}
|
|
77
|
+
sites={[
|
|
78
|
+
{
|
|
79
|
+
siteId: "demo",
|
|
80
|
+
label: "Demo site",
|
|
81
|
+
collections: [
|
|
82
|
+
{
|
|
83
|
+
name: "articles",
|
|
84
|
+
label: "Articles",
|
|
85
|
+
titleField: "title",
|
|
86
|
+
fields: [
|
|
87
|
+
{ name: "title", label: "Title", widget: "text", required: true },
|
|
88
|
+
{ name: "slug", label: "Slug", widget: "text", required: true },
|
|
89
|
+
{ name: "published", label: "Published", widget: "boolean" },
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
]}
|
|
95
|
+
/>,
|
|
96
|
+
document.getElementById("app")!,
|
|
97
|
+
);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## API requirements
|
|
101
|
+
|
|
102
|
+
Worker must expose (Phase 4 + ADR 018):
|
|
103
|
+
|
|
104
|
+
- `GET /v1/sites`
|
|
105
|
+
- `GET /v1/sites/:id/config`
|
|
106
|
+
- `GET .../content` (permission `read`) — **active** snapshot only for the granted site
|
|
107
|
+
- `POST .../validate` · `publish` · `rollback`
|
|
108
|
+
- `GET .../status`
|
|
109
|
+
|
|
110
|
+
Grant identities `read` plus the write permissions they need. CORS must allow the editor origin.
|
|
111
|
+
|
|
112
|
+
Publish from the editor always sends `expectedVersion` (optimistic concurrency) and an `idempotencyKey` so retries reuse Phase 3 guarantees.
|
|
113
|
+
|
|
114
|
+
**Content load size:** the stock editor loads the full active collection snapshot. Fine for typical brochure/portfolio collections; very large catalogues may need `entryId` fetches or a later pagination API (ADR 018).
|
|
115
|
+
|
|
116
|
+
## Drafts
|
|
117
|
+
|
|
118
|
+
Working copies may use in-memory state and `sessionStorage` for the current tab. Cross-device draft sync is not implemented.
|
|
119
|
+
|
|
120
|
+
## Example
|
|
121
|
+
|
|
122
|
+
See `examples/editor-demo` for an offline fake-API walkthrough and notes on pointing at a live Worker.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ContentSnapshotResponse, EditorApiResult, PublishResponse, SiteSummary, StatusResponse } from "./types.js";
|
|
2
|
+
export interface ContentRailApiClientOptions {
|
|
3
|
+
/** Base URL of the ContentRail Worker, e.g. `https://api.example.com`. */
|
|
4
|
+
readonly baseUrl: string;
|
|
5
|
+
/**
|
|
6
|
+
* Optional credential supplier. Prefer Access cookie/session forwarding
|
|
7
|
+
* (`credentials: "include"`) over storing JWTs in localStorage.
|
|
8
|
+
*/
|
|
9
|
+
readonly getAccessToken?: () => string | null | Promise<string | null>;
|
|
10
|
+
readonly fetchImpl?: typeof fetch;
|
|
11
|
+
readonly credentials?: RequestCredentials;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Browser-safe client for the Phase 4/5 Worker API.
|
|
15
|
+
* Never accepts Git/R2 credentials.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createContentRailApiClient(options: ContentRailApiClientOptions): {
|
|
18
|
+
listSites(): Promise<EditorApiResult<{
|
|
19
|
+
sites: SiteSummary[];
|
|
20
|
+
}>>;
|
|
21
|
+
getSiteConfig(siteId: string): Promise<EditorApiResult<{
|
|
22
|
+
siteId: string;
|
|
23
|
+
permissions: string[];
|
|
24
|
+
collections: string[];
|
|
25
|
+
}>>;
|
|
26
|
+
getContent(siteId: string, collection: string, entryId?: string): Promise<EditorApiResult<ContentSnapshotResponse>>;
|
|
27
|
+
getStatus(siteId: string, collection: string): Promise<EditorApiResult<StatusResponse>>;
|
|
28
|
+
validate(siteId: string, collection: string, body: {
|
|
29
|
+
expectedVersion: string | null;
|
|
30
|
+
entries: Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
data: unknown;
|
|
33
|
+
}>;
|
|
34
|
+
}): Promise<EditorApiResult<{
|
|
35
|
+
ok: true;
|
|
36
|
+
}>>;
|
|
37
|
+
publish(siteId: string, collection: string, body: {
|
|
38
|
+
expectedVersion: string | null;
|
|
39
|
+
entries: Array<{
|
|
40
|
+
id: string;
|
|
41
|
+
data: unknown;
|
|
42
|
+
}>;
|
|
43
|
+
idempotencyKey?: string;
|
|
44
|
+
message?: string;
|
|
45
|
+
}): Promise<EditorApiResult<PublishResponse>>;
|
|
46
|
+
rollback(siteId: string, collection: string, body: {
|
|
47
|
+
targetVersion: string;
|
|
48
|
+
expectedVersion: string | null;
|
|
49
|
+
}): Promise<EditorApiResult<PublishResponse>>;
|
|
50
|
+
};
|
|
51
|
+
export type ContentRailApiClient = ReturnType<typeof createContentRailApiClient>;
|
|
52
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,uBAAuB,EACvB,eAAe,EACf,eAAe,EACf,WAAW,EACX,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,2BAA2B;IAC1C,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAC3C;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,2BAA2B;;eAyER,WAAW,EAAE;;0BAEjB,MAAM;gBAEhB,MAAM;qBACD,MAAM,EAAE;qBACR,MAAM,EAAE;;uBAGN,MAAM,cAAc,MAAM,YAAY,MAAM;sBAQ7C,MAAM,cAAc,MAAM;qBAOlC,MAAM,cACF,MAAM,QACZ;QACJ,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,OAAO,EAAE,KAAK,CAAC;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;KAC/C;YAEoB,IAAI;;oBAOjB,MAAM,cACF,MAAM,QACZ;QACJ,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,OAAO,EAAE,KAAK,CAAC;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;QAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB;qBASO,MAAM,cACF,MAAM,QACZ;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;EASpE;AAED,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAC3C,OAAO,0BAA0B,CAClC,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe client for the Phase 4/5 Worker API.
|
|
3
|
+
* Never accepts Git/R2 credentials.
|
|
4
|
+
*/
|
|
5
|
+
export function createContentRailApiClient(options) {
|
|
6
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
7
|
+
const credentials = options.credentials ?? "include";
|
|
8
|
+
const base = options.baseUrl.replace(/\/+$/, "");
|
|
9
|
+
async function request(method, path, body) {
|
|
10
|
+
const headers = new Headers({ Accept: "application/json" });
|
|
11
|
+
if (body !== undefined) {
|
|
12
|
+
headers.set("Content-Type", "application/json");
|
|
13
|
+
}
|
|
14
|
+
const token = options.getAccessToken
|
|
15
|
+
? await options.getAccessToken()
|
|
16
|
+
: null;
|
|
17
|
+
if (token !== null && token !== "") {
|
|
18
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
19
|
+
}
|
|
20
|
+
let response;
|
|
21
|
+
try {
|
|
22
|
+
response = await fetchImpl(`${base}${path}`, {
|
|
23
|
+
method,
|
|
24
|
+
headers,
|
|
25
|
+
credentials,
|
|
26
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
status: 0,
|
|
33
|
+
code: "NETWORK",
|
|
34
|
+
message: "Network request failed. Check connectivity and API URL.",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const text = await response.text();
|
|
38
|
+
let parsed = null;
|
|
39
|
+
if (text !== "") {
|
|
40
|
+
try {
|
|
41
|
+
parsed = JSON.parse(text);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return {
|
|
45
|
+
ok: false,
|
|
46
|
+
status: response.status,
|
|
47
|
+
code: "INVALID_RESPONSE",
|
|
48
|
+
message: "API returned a non-JSON response.",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
const err = parsed;
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
status: response.status,
|
|
57
|
+
code: err?.error?.code ?? `HTTP_${response.status}`,
|
|
58
|
+
message: err?.error?.message ?? `Request failed (${response.status}).`,
|
|
59
|
+
...(err?.error?.currentVersion !== undefined
|
|
60
|
+
? { currentVersion: err.error.currentVersion }
|
|
61
|
+
: {}),
|
|
62
|
+
...(err?.requestId !== undefined ? { requestId: err.requestId } : {}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return { ok: true, status: response.status, data: parsed };
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
listSites() {
|
|
69
|
+
return request("GET", "/v1/sites");
|
|
70
|
+
},
|
|
71
|
+
getSiteConfig(siteId) {
|
|
72
|
+
return request("GET", `/v1/sites/${encodeURIComponent(siteId)}/config`);
|
|
73
|
+
},
|
|
74
|
+
getContent(siteId, collection, entryId) {
|
|
75
|
+
const query = entryId !== undefined ? `?entryId=${encodeURIComponent(entryId)}` : "";
|
|
76
|
+
return request("GET", `/v1/sites/${encodeURIComponent(siteId)}/collections/${encodeURIComponent(collection)}/content${query}`);
|
|
77
|
+
},
|
|
78
|
+
getStatus(siteId, collection) {
|
|
79
|
+
return request("GET", `/v1/sites/${encodeURIComponent(siteId)}/collections/${encodeURIComponent(collection)}/status`);
|
|
80
|
+
},
|
|
81
|
+
validate(siteId, collection, body) {
|
|
82
|
+
return request("POST", `/v1/sites/${encodeURIComponent(siteId)}/collections/${encodeURIComponent(collection)}/validate`, body);
|
|
83
|
+
},
|
|
84
|
+
publish(siteId, collection, body) {
|
|
85
|
+
return request("POST", `/v1/sites/${encodeURIComponent(siteId)}/collections/${encodeURIComponent(collection)}/publish`, body);
|
|
86
|
+
},
|
|
87
|
+
rollback(siteId, collection, body) {
|
|
88
|
+
return request("POST", `/v1/sites/${encodeURIComponent(siteId)}/collections/${encodeURIComponent(collection)}/rollback`, body);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAqBA;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAAoC;IAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC7C,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,SAAS,CAAC;IACrD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEjD,KAAK,UAAU,OAAO,CACpB,MAAc,EACd,IAAY,EACZ,IAAc;QAEd,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC5D,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;QAClD,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,cAAc;YAClC,CAAC,CAAC,MAAM,OAAO,CAAC,cAAc,EAAE;YAChC,CAAC,CAAC,IAAI,CAAC;QACT,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACnC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,KAAK,EAAE,CAAC,CAAC;QAClD,CAAC;QAED,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE;gBAC3C,MAAM;gBACN,OAAO;gBACP,WAAW;gBACX,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC9D,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,CAAC;gBACT,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,yDAAyD;aACnE,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,MAAM,GAAY,IAAI,CAAC;QAC3B,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAChB,IAAI,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;YACvC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,IAAI,EAAE,kBAAkB;oBACxB,OAAO,EAAE,mCAAmC;iBAC7C,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,GAAG,GAAG,MAA6B,CAAC;YAC1C,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,IAAI,QAAQ,QAAQ,CAAC,MAAM,EAAE;gBACnD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,IAAI,mBAAmB,QAAQ,CAAC,MAAM,IAAI;gBACtE,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,cAAc,KAAK,SAAS;oBAC1C,CAAC,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;oBAC9C,CAAC,CAAC,EAAE,CAAC;gBACP,GAAG,CAAC,GAAG,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACtE,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,MAAW,EAAE,CAAC;IAClE,CAAC;IAED,OAAO;QACL,SAAS;YACP,OAAO,OAAO,CAA2B,KAAK,EAAE,WAAW,CAAC,CAAC;QAC/D,CAAC;QACD,aAAa,CAAC,MAAc;YAC1B,OAAO,OAAO,CAIX,KAAK,EAAE,aAAa,kBAAkB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9D,CAAC;QACD,UAAU,CAAC,MAAc,EAAE,UAAkB,EAAE,OAAgB;YAC7D,MAAM,KAAK,GACT,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,OAAO,OAAO,CACZ,KAAK,EACL,aAAa,kBAAkB,CAAC,MAAM,CAAC,gBAAgB,kBAAkB,CAAC,UAAU,CAAC,WAAW,KAAK,EAAE,CACxG,CAAC;QACJ,CAAC;QACD,SAAS,CAAC,MAAc,EAAE,UAAkB;YAC1C,OAAO,OAAO,CACZ,KAAK,EACL,aAAa,kBAAkB,CAAC,MAAM,CAAC,gBAAgB,kBAAkB,CAAC,UAAU,CAAC,SAAS,CAC/F,CAAC;QACJ,CAAC;QACD,QAAQ,CACN,MAAc,EACd,UAAkB,EAClB,IAGC;YAED,OAAO,OAAO,CACZ,MAAM,EACN,aAAa,kBAAkB,CAAC,MAAM,CAAC,gBAAgB,kBAAkB,CAAC,UAAU,CAAC,WAAW,EAChG,IAAI,CACL,CAAC;QACJ,CAAC;QACD,OAAO,CACL,MAAc,EACd,UAAkB,EAClB,IAKC;YAED,OAAO,OAAO,CACZ,MAAM,EACN,aAAa,kBAAkB,CAAC,MAAM,CAAC,gBAAgB,kBAAkB,CAAC,UAAU,CAAC,UAAU,EAC/F,IAAI,CACL,CAAC;QACJ,CAAC;QACD,QAAQ,CACN,MAAc,EACd,UAAkB,EAClB,IAA+D;YAE/D,OAAO,OAAO,CACZ,MAAM,EACN,aAAa,kBAAkB,CAAC,MAAM,CAAC,gBAAgB,kBAAkB,CAAC,UAAU,CAAC,WAAW,EAChG,IAAI,CACL,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export interface ApiErrorBody {
|
|
2
|
+
readonly error: {
|
|
3
|
+
readonly code: string;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
readonly currentVersion?: string | null;
|
|
6
|
+
readonly expectedVersion?: string | null;
|
|
7
|
+
readonly issues?: unknown;
|
|
8
|
+
readonly entryId?: string;
|
|
9
|
+
};
|
|
10
|
+
readonly requestId?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface SiteSummary {
|
|
13
|
+
readonly siteId: string;
|
|
14
|
+
readonly permissions: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
export interface ContentSnapshotResponse {
|
|
17
|
+
readonly siteId: string;
|
|
18
|
+
readonly collection: string;
|
|
19
|
+
readonly version: string | null;
|
|
20
|
+
readonly publicationId: string | null;
|
|
21
|
+
readonly createdAt: string | null;
|
|
22
|
+
readonly entries: ReadonlyArray<{
|
|
23
|
+
id: string;
|
|
24
|
+
data: unknown;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
export interface StatusResponse {
|
|
28
|
+
readonly siteId: string;
|
|
29
|
+
readonly collection: string;
|
|
30
|
+
readonly activeVersion: string | null;
|
|
31
|
+
readonly versions: ReadonlyArray<{
|
|
32
|
+
version: string;
|
|
33
|
+
publicationId: string;
|
|
34
|
+
createdAt: string;
|
|
35
|
+
active: boolean;
|
|
36
|
+
}>;
|
|
37
|
+
}
|
|
38
|
+
export interface PublishResponse {
|
|
39
|
+
readonly success: true;
|
|
40
|
+
readonly publicationId: string;
|
|
41
|
+
readonly version: string;
|
|
42
|
+
readonly previousVersion: string | null;
|
|
43
|
+
readonly provider: {
|
|
44
|
+
kind: string;
|
|
45
|
+
revision: string;
|
|
46
|
+
};
|
|
47
|
+
readonly rebuild: {
|
|
48
|
+
state: string;
|
|
49
|
+
detail?: string;
|
|
50
|
+
triggerId?: string;
|
|
51
|
+
};
|
|
52
|
+
readonly idempotentReplay: boolean;
|
|
53
|
+
readonly manifest: {
|
|
54
|
+
collection: string;
|
|
55
|
+
version: string;
|
|
56
|
+
entryIds: readonly string[];
|
|
57
|
+
createdAt: string;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export type EditorApiResult<T> = {
|
|
61
|
+
ok: true;
|
|
62
|
+
data: T;
|
|
63
|
+
status: number;
|
|
64
|
+
} | {
|
|
65
|
+
ok: false;
|
|
66
|
+
status: number;
|
|
67
|
+
code: string;
|
|
68
|
+
message: string;
|
|
69
|
+
currentVersion?: string | null;
|
|
70
|
+
requestId?: string;
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACxC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;QAC/B,OAAO,EAAE,MAAM,CAAC;QAChB,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,QAAQ,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzE,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;QAC5B,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,IACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrC;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EditorFieldSpec } from "./types.js";
|
|
2
|
+
/** Build a blank value tree for create-entry forms. */
|
|
3
|
+
export declare function emptyValueForFields(fields: readonly EditorFieldSpec[]): Record<string, unknown>;
|
|
4
|
+
export declare function emptyValueForField(field: EditorFieldSpec): unknown;
|
|
5
|
+
export declare function sortFields(fields: readonly EditorFieldSpec[]): EditorFieldSpec[];
|
|
6
|
+
/** Client-side required/shape checks. Server validation remains authoritative. */
|
|
7
|
+
export declare function clientValidateFields(fields: readonly EditorFieldSpec[], data: unknown, path?: string[]): Array<{
|
|
8
|
+
path: string;
|
|
9
|
+
message: string;
|
|
10
|
+
}>;
|
|
11
|
+
//# sourceMappingURL=defaults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/fields/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,uDAAuD;AACvD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,eAAe,EAAE,GACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAoBlE;AAED,wBAAgB,UAAU,CACxB,MAAM,EAAE,SAAS,eAAe,EAAE,GACjC,eAAe,EAAE,CAKnB;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,eAAe,EAAE,EAClC,IAAI,EAAE,OAAO,EACb,IAAI,GAAE,MAAM,EAAO,GAClB,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA6C1C"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** Build a blank value tree for create-entry forms. */
|
|
2
|
+
export function emptyValueForFields(fields) {
|
|
3
|
+
const result = {};
|
|
4
|
+
for (const field of sortFields(fields)) {
|
|
5
|
+
result[field.name] = emptyValueForField(field);
|
|
6
|
+
}
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
export function emptyValueForField(field) {
|
|
10
|
+
if (field.widget === "text" || field.widget === "textarea") {
|
|
11
|
+
return "";
|
|
12
|
+
}
|
|
13
|
+
if (field.widget === "number") {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
if (field.widget === "boolean") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (field.widget === "enum") {
|
|
20
|
+
return field.options[0]?.value ?? "";
|
|
21
|
+
}
|
|
22
|
+
if (field.widget === "object") {
|
|
23
|
+
return emptyValueForFields(field.fields);
|
|
24
|
+
}
|
|
25
|
+
if (field.widget === "array") {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
export function sortFields(fields) {
|
|
31
|
+
return [...fields].sort((a, b) => (a.order ?? 100) - (b.order ?? 100) || a.name.localeCompare(b.name));
|
|
32
|
+
}
|
|
33
|
+
/** Client-side required/shape checks. Server validation remains authoritative. */
|
|
34
|
+
export function clientValidateFields(fields, data, path = []) {
|
|
35
|
+
const issues = [];
|
|
36
|
+
if (data === null || typeof data !== "object" || Array.isArray(data)) {
|
|
37
|
+
issues.push({
|
|
38
|
+
path: path.join(".") || "(root)",
|
|
39
|
+
message: "Entry data must be an object.",
|
|
40
|
+
});
|
|
41
|
+
return issues;
|
|
42
|
+
}
|
|
43
|
+
const record = data;
|
|
44
|
+
for (const field of fields) {
|
|
45
|
+
const fieldPath = [...path, field.name].join(".");
|
|
46
|
+
const value = record[field.name];
|
|
47
|
+
if (field.widget === "unsupported") {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (field.required && isEmpty(value, field)) {
|
|
51
|
+
issues.push({ path: fieldPath, message: `${field.label} is required.` });
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (field.widget === "object" && value !== undefined && value !== null) {
|
|
55
|
+
issues.push(...clientValidateFields(field.fields, value, [...path, field.name]));
|
|
56
|
+
}
|
|
57
|
+
if (field.widget === "array" && Array.isArray(value)) {
|
|
58
|
+
value.forEach((item, index) => {
|
|
59
|
+
if (field.item.widget === "object") {
|
|
60
|
+
issues.push(...clientValidateFields(field.item.fields, item, [
|
|
61
|
+
...path,
|
|
62
|
+
field.name,
|
|
63
|
+
String(index),
|
|
64
|
+
]));
|
|
65
|
+
}
|
|
66
|
+
else if (field.item.required && isEmpty(item, field.item)) {
|
|
67
|
+
issues.push({
|
|
68
|
+
path: `${fieldPath}.${index}`,
|
|
69
|
+
message: `${field.label} item is required.`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return issues;
|
|
76
|
+
}
|
|
77
|
+
function isEmpty(value, field) {
|
|
78
|
+
if (value === undefined || value === null) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
if ((field.widget === "text" ||
|
|
82
|
+
field.widget === "textarea" ||
|
|
83
|
+
field.widget === "enum") &&
|
|
84
|
+
typeof value === "string" &&
|
|
85
|
+
value.trim() === "") {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
if (field.widget === "array" && Array.isArray(value) && value.length === 0) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/fields/defaults.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Explicit editor field metadata (ADR 019).
|
|
3
|
+
* Complements Zod schemas — does not introspect Zod internals.
|
|
4
|
+
*/
|
|
5
|
+
export type EditorWidget = "text" | "textarea" | "number" | "boolean" | "enum" | "object" | "array" | "unsupported";
|
|
6
|
+
export interface EditorFieldBase {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly label: string;
|
|
9
|
+
readonly helpText?: string;
|
|
10
|
+
readonly required?: boolean;
|
|
11
|
+
readonly readOnly?: boolean;
|
|
12
|
+
/** Lower values appear first. Default 100. */
|
|
13
|
+
readonly order?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface TextFieldSpec extends EditorFieldBase {
|
|
16
|
+
readonly widget: "text" | "textarea";
|
|
17
|
+
readonly placeholder?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface NumberFieldSpec extends EditorFieldBase {
|
|
20
|
+
readonly widget: "number";
|
|
21
|
+
readonly min?: number;
|
|
22
|
+
readonly max?: number;
|
|
23
|
+
readonly step?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface BooleanFieldSpec extends EditorFieldBase {
|
|
26
|
+
readonly widget: "boolean";
|
|
27
|
+
}
|
|
28
|
+
export interface EnumFieldSpec extends EditorFieldBase {
|
|
29
|
+
readonly widget: "enum";
|
|
30
|
+
readonly options: ReadonlyArray<{
|
|
31
|
+
value: string;
|
|
32
|
+
label: string;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export interface ObjectFieldSpec extends EditorFieldBase {
|
|
36
|
+
readonly widget: "object";
|
|
37
|
+
readonly fields: readonly EditorFieldSpec[];
|
|
38
|
+
}
|
|
39
|
+
export interface ArrayFieldSpec extends EditorFieldBase {
|
|
40
|
+
readonly widget: "array";
|
|
41
|
+
/** Spec for each array item (object or scalar widget). */
|
|
42
|
+
readonly item: EditorFieldSpec;
|
|
43
|
+
}
|
|
44
|
+
export interface UnsupportedFieldSpec extends EditorFieldBase {
|
|
45
|
+
readonly widget: "unsupported";
|
|
46
|
+
readonly reason: string;
|
|
47
|
+
}
|
|
48
|
+
export type EditorFieldSpec = TextFieldSpec | NumberFieldSpec | BooleanFieldSpec | EnumFieldSpec | ObjectFieldSpec | ArrayFieldSpec | UnsupportedFieldSpec;
|
|
49
|
+
export interface EditorCollectionConfig {
|
|
50
|
+
readonly name: string;
|
|
51
|
+
readonly label: string;
|
|
52
|
+
readonly description?: string;
|
|
53
|
+
/** Field used as the human-readable entry title in lists. */
|
|
54
|
+
readonly titleField?: string;
|
|
55
|
+
readonly fields: readonly EditorFieldSpec[];
|
|
56
|
+
}
|
|
57
|
+
export interface EditorSiteConfig {
|
|
58
|
+
readonly siteId: string;
|
|
59
|
+
readonly label: string;
|
|
60
|
+
readonly collections: readonly EditorCollectionConfig[];
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/fields/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,UAAU,GACV,QAAQ,GACR,SAAS,GACT,MAAM,GACN,QAAQ,GACR,OAAO,GACP,aAAa,CAAC;AAElB,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAc,SAAQ,eAAe;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,aAAc,SAAQ,eAAe;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnE;AAED,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,cAAe,SAAQ,eAAe;IACrD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;CAChC;AAED,MAAM,WAAW,oBAAqB,SAAQ,eAAe;IAC3D,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,eAAe,GACvB,aAAa,GACb,eAAe,GACf,gBAAgB,GAChB,aAAa,GACb,eAAe,GACf,cAAc,GACd,oBAAoB,CAAC;AAEzB,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,SAAS,sBAAsB,EAAE,CAAC;CACzD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/fields/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EditorFieldSpec } from "../fields/types.js";
|
|
2
|
+
export interface FieldControlProps {
|
|
3
|
+
readonly field: EditorFieldSpec;
|
|
4
|
+
readonly value: unknown;
|
|
5
|
+
readonly path: string;
|
|
6
|
+
readonly error?: string;
|
|
7
|
+
readonly onChange: (path: string, value: unknown) => void;
|
|
8
|
+
readonly disabled?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function FieldControl(props: FieldControlProps): import("preact").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=FieldControl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldControl.d.ts","sourceRoot":"","sources":["../../src/form/FieldControl.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAG1D,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,gCAsIpD"}
|