@cedros/data-react 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/CHANGELOG.md +24 -0
- package/README.md +177 -0
- package/dist/admin/api.d.ts +19 -0
- package/dist/admin/api.js +108 -0
- package/dist/admin/components.d.ts +36 -0
- package/dist/admin/components.js +22 -0
- package/dist/admin/history.d.ts +17 -0
- package/dist/admin/history.js +103 -0
- package/dist/admin/icons.d.ts +15 -0
- package/dist/admin/icons.js +18 -0
- package/dist/admin/index.d.ts +13 -0
- package/dist/admin/index.js +12 -0
- package/dist/admin/permissions.d.ts +4 -0
- package/dist/admin/permissions.js +45 -0
- package/dist/admin/plugin.d.ts +4 -0
- package/dist/admin/plugin.js +180 -0
- package/dist/admin/primitives/ConfirmDialog.d.ts +14 -0
- package/dist/admin/primitives/ConfirmDialog.js +7 -0
- package/dist/admin/primitives/DataTable.d.ts +14 -0
- package/dist/admin/primitives/DataTable.js +7 -0
- package/dist/admin/primitives/DiffViewer.d.ts +11 -0
- package/dist/admin/primitives/DiffViewer.js +8 -0
- package/dist/admin/primitives/FormFieldRow.d.ts +23 -0
- package/dist/admin/primitives/FormFieldRow.js +16 -0
- package/dist/admin/primitives/JsonCodeEditor.d.ts +10 -0
- package/dist/admin/primitives/JsonCodeEditor.js +42 -0
- package/dist/admin/primitives/Pagination.d.ts +8 -0
- package/dist/admin/primitives/Pagination.js +8 -0
- package/dist/admin/primitives/Toolbar.d.ts +23 -0
- package/dist/admin/primitives/Toolbar.js +10 -0
- package/dist/admin/primitives/alerts.d.ts +21 -0
- package/dist/admin/primitives/alerts.js +44 -0
- package/dist/admin/sectionIds.d.ts +20 -0
- package/dist/admin/sectionIds.js +33 -0
- package/dist/admin/sections/CollectionsSection.d.ts +2 -0
- package/dist/admin/sections/CollectionsSection.js +125 -0
- package/dist/admin/sections/ContractVerifySection.d.ts +11 -0
- package/dist/admin/sections/ContractVerifySection.js +98 -0
- package/dist/admin/sections/CustomDataSection.d.ts +2 -0
- package/dist/admin/sections/CustomDataSection.js +256 -0
- package/dist/admin/sections/DataOpsSection.d.ts +26 -0
- package/dist/admin/sections/DataOpsSection.js +245 -0
- package/dist/admin/sections/HistorySection.d.ts +2 -0
- package/dist/admin/sections/HistorySection.js +26 -0
- package/dist/admin/sections/MonetizationSection.d.ts +2 -0
- package/dist/admin/sections/MonetizationSection.js +140 -0
- package/dist/admin/sections/NavigationSection.d.ts +13 -0
- package/dist/admin/sections/NavigationSection.js +195 -0
- package/dist/admin/sections/PagesSection.d.ts +2 -0
- package/dist/admin/sections/PagesSection.js +157 -0
- package/dist/admin/sections/SchemaDesignerSection.d.ts +2 -0
- package/dist/admin/sections/SchemaDesignerSection.js +167 -0
- package/dist/admin/sections/SiteSettingsSection.d.ts +12 -0
- package/dist/admin/sections/SiteSettingsSection.js +122 -0
- package/dist/admin/sections/TippingSection.d.ts +2 -0
- package/dist/admin/sections/TippingSection.js +178 -0
- package/dist/admin/sections/media/MediaDetail.d.ts +12 -0
- package/dist/admin/sections/media/MediaDetail.js +74 -0
- package/dist/admin/sections/media/MediaGrid.d.ts +14 -0
- package/dist/admin/sections/media/MediaGrid.js +22 -0
- package/dist/admin/sections/media/MediaSection.d.ts +2 -0
- package/dist/admin/sections/media/MediaSection.js +97 -0
- package/dist/admin/sections/media/MediaUploader.d.ts +7 -0
- package/dist/admin/sections/media/MediaUploader.js +72 -0
- package/dist/admin/sections/media/types.d.ts +33 -0
- package/dist/admin/sections/media/types.js +1 -0
- package/dist/admin/styles.css +533 -0
- package/dist/admin/types.d.ts +85 -0
- package/dist/admin/types.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/react/CmsContent.d.ts +20 -0
- package/dist/react/CmsContent.js +31 -0
- package/dist/react/entries.d.ts +9 -0
- package/dist/react/entries.js +25 -0
- package/dist/react/fetch.d.ts +11 -0
- package/dist/react/fetch.js +32 -0
- package/dist/react/index.d.ts +10 -0
- package/dist/react/index.js +9 -0
- package/dist/react/metadata.d.ts +44 -0
- package/dist/react/metadata.js +142 -0
- package/dist/react/sanitize.d.ts +17 -0
- package/dist/react/sanitize.js +326 -0
- package/dist/react/server.d.ts +14 -0
- package/dist/react/server.js +13 -0
- package/dist/react/sitemap.d.ts +28 -0
- package/dist/react/sitemap.js +91 -0
- package/dist/react/slugs.d.ts +27 -0
- package/dist/react/slugs.js +52 -0
- package/dist/react/types.d.ts +85 -0
- package/dist/react/types.js +1 -0
- package/dist/react/visitor.d.ts +7 -0
- package/dist/react/visitor.js +18 -0
- package/dist/site-templates/BlogTemplates.d.ts +95 -0
- package/dist/site-templates/BlogTemplates.js +64 -0
- package/dist/site-templates/ContactPageTemplate.d.ts +14 -0
- package/dist/site-templates/ContactPageTemplate.js +5 -0
- package/dist/site-templates/DashboardOverviewTemplate.d.ts +29 -0
- package/dist/site-templates/DashboardOverviewTemplate.js +17 -0
- package/dist/site-templates/DashboardShell.d.ts +28 -0
- package/dist/site-templates/DashboardShell.js +10 -0
- package/dist/site-templates/DocsSidebar.d.ts +14 -0
- package/dist/site-templates/DocsSidebar.js +13 -0
- package/dist/site-templates/DocsTemplates.d.ts +60 -0
- package/dist/site-templates/DocsTemplates.js +47 -0
- package/dist/site-templates/HomePageTemplate.d.ts +15 -0
- package/dist/site-templates/HomePageTemplate.js +10 -0
- package/dist/site-templates/LegalPageTemplate.d.ts +12 -0
- package/dist/site-templates/LegalPageTemplate.js +6 -0
- package/dist/site-templates/MarkdownContent.d.ts +7 -0
- package/dist/site-templates/MarkdownContent.js +24 -0
- package/dist/site-templates/NotFoundTemplate.d.ts +9 -0
- package/dist/site-templates/NotFoundTemplate.js +5 -0
- package/dist/site-templates/SiteFooter.d.ts +13 -0
- package/dist/site-templates/SiteFooter.js +4 -0
- package/dist/site-templates/SiteLayout.d.ts +14 -0
- package/dist/site-templates/SiteLayout.js +6 -0
- package/dist/site-templates/TopNav.d.ts +10 -0
- package/dist/site-templates/TopNav.js +8 -0
- package/dist/site-templates/blogControls.d.ts +19 -0
- package/dist/site-templates/blogControls.js +37 -0
- package/dist/site-templates/codeBlock.d.ts +9 -0
- package/dist/site-templates/codeBlock.js +31 -0
- package/dist/site-templates/content-styles.css +410 -0
- package/dist/site-templates/contentIndex.d.ts +65 -0
- package/dist/site-templates/contentIndex.js +181 -0
- package/dist/site-templates/contentUi.d.ts +14 -0
- package/dist/site-templates/contentUi.js +24 -0
- package/dist/site-templates/docs-styles.css +259 -0
- package/dist/site-templates/docsNavigation.d.ts +18 -0
- package/dist/site-templates/docsNavigation.js +50 -0
- package/dist/site-templates/index.d.ts +28 -0
- package/dist/site-templates/index.js +25 -0
- package/dist/site-templates/monetization-styles.css +154 -0
- package/dist/site-templates/paywallControls.d.ts +22 -0
- package/dist/site-templates/paywallControls.js +9 -0
- package/dist/site-templates/routing.d.ts +12 -0
- package/dist/site-templates/routing.js +36 -0
- package/dist/site-templates/solanaAtaSetup.d.ts +11 -0
- package/dist/site-templates/solanaAtaSetup.js +38 -0
- package/dist/site-templates/solanaMicropayments.d.ts +65 -0
- package/dist/site-templates/solanaMicropayments.js +115 -0
- package/dist/site-templates/styles.css +332 -0
- package/dist/site-templates/tipControls.d.ts +24 -0
- package/dist/site-templates/tipControls.js +43 -0
- package/dist/site-templates/tocExtractor.d.ts +16 -0
- package/dist/site-templates/tocExtractor.js +58 -0
- package/dist/site-templates/tocScrollSpy.d.ts +16 -0
- package/dist/site-templates/tocScrollSpy.js +37 -0
- package/dist/templates.d.ts +8 -0
- package/dist/templates.js +20 -0
- package/package.json +58 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `MarkdownContent` renderer based on `react-markdown` + `remark-gfm` + `rehype-slug`.
|
|
7
|
+
- `bodyMarkdown` support in:
|
|
8
|
+
- `BlogPostTemplate`
|
|
9
|
+
- `DocArticleTemplate`
|
|
10
|
+
- `LegalPageTemplate`
|
|
11
|
+
- `allowUnsafeHtmlFallback` opt-in for legacy raw HTML rendering.
|
|
12
|
+
- GitBook-style docs navigation helpers and layout support (`buildDocsSidebarSections`, `withActiveDocsSidebar`).
|
|
13
|
+
- End-to-end example for loading markdown from `cedros-data` and rendering docs templates.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- `bodyHtml` is no longer the default rendering path for article/legal templates.
|
|
17
|
+
- Raw HTML now requires explicit `allowUnsafeHtmlFallback={true}`.
|
|
18
|
+
|
|
19
|
+
### Migration Notes
|
|
20
|
+
- Before:
|
|
21
|
+
- `bodyHtml` as primary content input.
|
|
22
|
+
- After:
|
|
23
|
+
- use `bodyMarkdown` as primary input.
|
|
24
|
+
- keep `bodyHtml` only for compatibility and set `allowUnsafeHtmlFallback={true}` if absolutely required.
|
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# @cedros/data-react
|
|
2
|
+
|
|
3
|
+
`@cedros/data-react` is the frontend companion package for `cedros-data`.
|
|
4
|
+
|
|
5
|
+
It provides:
|
|
6
|
+
- a cedros-login `AdminShell` plugin (`cedrosDataPlugin`)
|
|
7
|
+
- reusable admin primitives/components
|
|
8
|
+
- reusable site layout components
|
|
9
|
+
- page templates for core/content/legal routes
|
|
10
|
+
- GitBook-style docs rendering with markdown support
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
cd ui
|
|
16
|
+
npm install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Build and test
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm run check
|
|
23
|
+
npm run build
|
|
24
|
+
npm test
|
|
25
|
+
npm run test:packaged
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Packaging smoke:
|
|
29
|
+
- `npm pack --dry-run` now runs `prepack`, which rebuilds `dist/` and verifies every exported JS/CSS/types file before the tarball is assembled
|
|
30
|
+
|
|
31
|
+
## Package exports
|
|
32
|
+
|
|
33
|
+
- `@cedros/data-react`
|
|
34
|
+
- `defaultPageTemplates`
|
|
35
|
+
- `DefaultPageKey`, `PageTemplateContract`
|
|
36
|
+
- re-exports admin + site-template modules
|
|
37
|
+
- `@cedros/data-react/admin`
|
|
38
|
+
- `cedrosDataPlugin`
|
|
39
|
+
- section IDs/groups/types
|
|
40
|
+
- admin components/primitives
|
|
41
|
+
- `@cedros/data-react/site-templates`
|
|
42
|
+
- site shell/layout components
|
|
43
|
+
- page templates
|
|
44
|
+
- routing/content helpers
|
|
45
|
+
- `@cedros/data-react/admin/styles.css`
|
|
46
|
+
- `@cedros/data-react/site-templates/styles.css`
|
|
47
|
+
|
|
48
|
+
Runtime packaging note:
|
|
49
|
+
- the published JS entrypoints are side-effect-free so they stay importable in Node/SSR
|
|
50
|
+
- import `@cedros/data-react/admin/styles.css` and/or `@cedros/data-react/site-templates/styles.css` explicitly in browser apps that need the packaged styles
|
|
51
|
+
|
|
52
|
+
Publish surface:
|
|
53
|
+
- npm tarballs intentionally include only `dist/`, `README.md`, and `CHANGELOG.md`
|
|
54
|
+
- source files, tests, examples, and local task notes stay out of the published package
|
|
55
|
+
|
|
56
|
+
## AdminShell integration
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { AdminShell, cedrosLoginPlugin } from "@cedros/login-react";
|
|
60
|
+
import { cedrosDataPlugin } from "@cedros/data-react/admin";
|
|
61
|
+
|
|
62
|
+
<AdminShell
|
|
63
|
+
plugins={[cedrosLoginPlugin, cedrosDataPlugin]}
|
|
64
|
+
hostContext={{
|
|
65
|
+
cedrosLogin: {
|
|
66
|
+
user,
|
|
67
|
+
getAccessToken,
|
|
68
|
+
serverUrl: "http://localhost:8080"
|
|
69
|
+
},
|
|
70
|
+
org: { orgId: "11111111-1111-1111-1111-111111111111", role: "owner", permissions: [] },
|
|
71
|
+
custom: {
|
|
72
|
+
cedrosData: {
|
|
73
|
+
serverUrl: "http://localhost:8080"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}}
|
|
77
|
+
/>;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Plugin context behavior:
|
|
81
|
+
- resolves `serverUrl` from `custom.cedrosData.serverUrl` or `cedrosLogin.serverUrl`
|
|
82
|
+
- forwards bearer token from `cedrosLogin.getAccessToken()`
|
|
83
|
+
- forwards `x-cedros-org-id` when `hostContext.org.orgId` is present
|
|
84
|
+
|
|
85
|
+
## Admin sections
|
|
86
|
+
|
|
87
|
+
Content:
|
|
88
|
+
- `pages`
|
|
89
|
+
- `navigation`
|
|
90
|
+
- `site-settings`
|
|
91
|
+
|
|
92
|
+
Data model:
|
|
93
|
+
- `collections`
|
|
94
|
+
- `schema-designer`
|
|
95
|
+
- `contract-verify`
|
|
96
|
+
- `custom-data`
|
|
97
|
+
|
|
98
|
+
Operations:
|
|
99
|
+
- `data-ops`
|
|
100
|
+
- `history`
|
|
101
|
+
|
|
102
|
+
## Styling and dark mode
|
|
103
|
+
|
|
104
|
+
- Admin UIs use tokenized variables aligned with cedros-login/cedros-pay conventions.
|
|
105
|
+
- Dark mode responds to both:
|
|
106
|
+
- `.cedros-dark`
|
|
107
|
+
- `.cedros-admin--dark`
|
|
108
|
+
- Direct import option:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import "@cedros/data-react/admin/styles.css";
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Site templates and components
|
|
115
|
+
|
|
116
|
+
Layouts/navigation:
|
|
117
|
+
- `SiteLayout`
|
|
118
|
+
- `TopNav`
|
|
119
|
+
- `SiteFooter`
|
|
120
|
+
- `DashboardShell`
|
|
121
|
+
|
|
122
|
+
Core page templates:
|
|
123
|
+
- `HomePageTemplate`
|
|
124
|
+
- `ContactPageTemplate`
|
|
125
|
+
- `LegalPageTemplate`
|
|
126
|
+
- `NotFoundTemplate`
|
|
127
|
+
- `DashboardOverviewTemplate`
|
|
128
|
+
|
|
129
|
+
Blog templates:
|
|
130
|
+
- `BlogIndexTemplate`
|
|
131
|
+
- `BlogPostTemplate`
|
|
132
|
+
|
|
133
|
+
Docs templates:
|
|
134
|
+
- `DocsIndexTemplate`
|
|
135
|
+
- `DocArticleTemplate` (GitBook-style left nav + right TOC)
|
|
136
|
+
|
|
137
|
+
Content rendering and helpers:
|
|
138
|
+
- `MarkdownContent`
|
|
139
|
+
- `Breadcrumbs`
|
|
140
|
+
- `ContentPagination`
|
|
141
|
+
- `withActiveRouteState`
|
|
142
|
+
- `buildDocsSidebarSections`
|
|
143
|
+
- `withActiveDocsSidebar`
|
|
144
|
+
- `prepareBlogIndex`
|
|
145
|
+
- `prepareDocsIndex`
|
|
146
|
+
- `collectFilterValues`
|
|
147
|
+
- `buildContentListHref`
|
|
148
|
+
|
|
149
|
+
## Markdown and HTML behavior
|
|
150
|
+
|
|
151
|
+
Docs/blog templates default to `bodyMarkdown`.
|
|
152
|
+
|
|
153
|
+
`bodyHtml` remains compatibility fallback and is only rendered when:
|
|
154
|
+
- `allowUnsafeHtmlFallback={true}`
|
|
155
|
+
|
|
156
|
+
This keeps markdown as the safe default and avoids unsafe HTML rendering by default.
|
|
157
|
+
|
|
158
|
+
## Default page contract
|
|
159
|
+
|
|
160
|
+
The package exports the default page template contract used by server bootstrap:
|
|
161
|
+
- `home`
|
|
162
|
+
- `about`
|
|
163
|
+
- `contact`
|
|
164
|
+
- `docs`
|
|
165
|
+
- `blog`
|
|
166
|
+
- `privacy-policy`
|
|
167
|
+
- `terms-of-service`
|
|
168
|
+
- `not-found`
|
|
169
|
+
|
|
170
|
+
## Example
|
|
171
|
+
|
|
172
|
+
End-to-end markdown integration example:
|
|
173
|
+
- `examples/cedros-data-markdown-e2e.tsx`
|
|
174
|
+
- the example reads page records from `GET /admin/pages` and uses each record's `entry_key`
|
|
175
|
+
|
|
176
|
+
See also:
|
|
177
|
+
- `CHANGELOG.md`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PluginContext } from "./types.js";
|
|
2
|
+
interface RequestOptions {
|
|
3
|
+
method?: string;
|
|
4
|
+
body?: unknown;
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function requestJson<T>(pluginContext: PluginContext, path: string, options?: RequestOptions): Promise<T>;
|
|
9
|
+
export interface LatestRequest {
|
|
10
|
+
signal: AbortSignal;
|
|
11
|
+
isCurrent: () => boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface LatestRequestController {
|
|
14
|
+
begin: () => LatestRequest;
|
|
15
|
+
cancel: () => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function createLatestRequestController(): LatestRequestController;
|
|
18
|
+
export declare function uploadFile<T>(pluginContext: PluginContext, path: string, formData: FormData): Promise<T>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|
|
2
|
+
export async function requestJson(pluginContext, path, options = {}) {
|
|
3
|
+
const token = pluginContext.getAccessToken();
|
|
4
|
+
const headers = {
|
|
5
|
+
"Content-Type": "application/json"
|
|
6
|
+
};
|
|
7
|
+
if (token) {
|
|
8
|
+
headers.Authorization = `Bearer ${token}`;
|
|
9
|
+
}
|
|
10
|
+
if (pluginContext.orgId) {
|
|
11
|
+
headers["x-cedros-org-id"] = pluginContext.orgId;
|
|
12
|
+
}
|
|
13
|
+
const baseUrl = pluginContext.serverUrl.replace(/\/+$/, "");
|
|
14
|
+
const requestPath = path.startsWith("/") ? path : `/${path}`;
|
|
15
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
16
|
+
const controller = new AbortController();
|
|
17
|
+
const onAbort = () => controller.abort();
|
|
18
|
+
let timedOut = false;
|
|
19
|
+
let timeoutId;
|
|
20
|
+
if (options.signal) {
|
|
21
|
+
if (options.signal.aborted) {
|
|
22
|
+
controller.abort();
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (timeoutMs > 0) {
|
|
29
|
+
timeoutId = setTimeout(() => {
|
|
30
|
+
timedOut = true;
|
|
31
|
+
controller.abort();
|
|
32
|
+
}, timeoutMs);
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch(`${baseUrl}${requestPath}`, {
|
|
36
|
+
method: options.method ?? "GET",
|
|
37
|
+
headers,
|
|
38
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
39
|
+
signal: controller.signal
|
|
40
|
+
});
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
const text = await response.text();
|
|
43
|
+
throw new Error(text || `Request failed (${response.status})`);
|
|
44
|
+
}
|
|
45
|
+
return (await response.json());
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (timedOut && isAbortError(error)) {
|
|
49
|
+
throw new Error(`Request timed out after ${timeoutMs}ms`);
|
|
50
|
+
}
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
if (timeoutId !== undefined) {
|
|
55
|
+
clearTimeout(timeoutId);
|
|
56
|
+
}
|
|
57
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export function createLatestRequestController() {
|
|
61
|
+
let currentToken = 0;
|
|
62
|
+
let currentController = null;
|
|
63
|
+
return {
|
|
64
|
+
begin() {
|
|
65
|
+
currentToken += 1;
|
|
66
|
+
currentController?.abort();
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
currentController = controller;
|
|
69
|
+
const requestToken = currentToken;
|
|
70
|
+
return {
|
|
71
|
+
signal: controller.signal,
|
|
72
|
+
isCurrent: () => currentController === controller &&
|
|
73
|
+
currentToken === requestToken &&
|
|
74
|
+
!controller.signal.aborted
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
cancel() {
|
|
78
|
+
currentToken += 1;
|
|
79
|
+
currentController?.abort();
|
|
80
|
+
currentController = null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export async function uploadFile(pluginContext, path, formData) {
|
|
85
|
+
const token = pluginContext.getAccessToken();
|
|
86
|
+
const headers = {};
|
|
87
|
+
if (token) {
|
|
88
|
+
headers.Authorization = `Bearer ${token}`;
|
|
89
|
+
}
|
|
90
|
+
if (pluginContext.orgId) {
|
|
91
|
+
headers["x-cedros-org-id"] = pluginContext.orgId;
|
|
92
|
+
}
|
|
93
|
+
const baseUrl = pluginContext.serverUrl.replace(/\/+$/, "");
|
|
94
|
+
const requestPath = path.startsWith("/") ? path : `/${path}`;
|
|
95
|
+
const response = await fetch(`${baseUrl}${requestPath}`, {
|
|
96
|
+
method: "POST",
|
|
97
|
+
headers,
|
|
98
|
+
body: formData
|
|
99
|
+
});
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
const text = await response.text();
|
|
102
|
+
throw new Error(text || `Upload failed (${response.status})`);
|
|
103
|
+
}
|
|
104
|
+
return (await response.json());
|
|
105
|
+
}
|
|
106
|
+
function isAbortError(error) {
|
|
107
|
+
return error instanceof Error && error.name === "AbortError";
|
|
108
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import type { ButtonHTMLAttributes, InputHTMLAttributes, ReactNode, SelectHTMLAttributes, TextareaHTMLAttributes } from "react";
|
|
3
|
+
type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
|
|
4
|
+
type ButtonSize = "sm" | "md";
|
|
5
|
+
interface AdminButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
6
|
+
variant?: ButtonVariant;
|
|
7
|
+
size?: ButtonSize;
|
|
8
|
+
}
|
|
9
|
+
export declare function AdminButton({ className, variant, size, ...rest }: AdminButtonProps): React.JSX.Element;
|
|
10
|
+
interface CardProps {
|
|
11
|
+
title?: string;
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
actions?: ReactNode;
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
export declare function Card({ title, subtitle, actions, children }: CardProps): React.JSX.Element;
|
|
17
|
+
interface TextInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
18
|
+
label?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function TextInput({ label, className, ...rest }: TextInputProps): React.JSX.Element;
|
|
21
|
+
interface SelectInputProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
|
22
|
+
label?: string;
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
export declare function SelectInput({ label, className, children, ...rest }: SelectInputProps): React.JSX.Element;
|
|
26
|
+
interface JsonEditorProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
27
|
+
label?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function JsonEditor({ label, className, ...rest }: JsonEditorProps): React.JSX.Element;
|
|
30
|
+
type StatusTone = "neutral" | "success" | "error";
|
|
31
|
+
interface StatusNoticeProps {
|
|
32
|
+
tone: StatusTone;
|
|
33
|
+
message: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function StatusNotice({ tone, message }: StatusNoticeProps): React.JSX.Element;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
function cx(...parts) {
|
|
3
|
+
return parts.filter(Boolean).join(" ");
|
|
4
|
+
}
|
|
5
|
+
export function AdminButton({ className, variant = "secondary", size = "md", ...rest }) {
|
|
6
|
+
return (_jsx("button", { className: cx("cedros-data-btn", `cedros-data-btn--${variant}`, size === "sm" ? "cedros-data-btn--sm" : undefined, className), ...rest }));
|
|
7
|
+
}
|
|
8
|
+
export function Card({ title, subtitle, actions, children }) {
|
|
9
|
+
return (_jsxs("section", { className: "cedros-data-card", children: [(title || subtitle || actions) && (_jsxs("header", { className: "cedros-data-card__header", children: [_jsxs("div", { children: [title && _jsx("h3", { className: "cedros-data-card__title", children: title }), subtitle && _jsx("p", { className: "cedros-data-card__subtitle", children: subtitle })] }), actions] })), _jsx("div", { className: "cedros-data-card__body", children: children })] }));
|
|
10
|
+
}
|
|
11
|
+
export function TextInput({ label, className, ...rest }) {
|
|
12
|
+
return (_jsxs("label", { className: "cedros-data-field", children: [label && _jsx("span", { className: "cedros-data-label", children: label }), _jsx("input", { className: cx("cedros-data-input", className), ...rest })] }));
|
|
13
|
+
}
|
|
14
|
+
export function SelectInput({ label, className, children, ...rest }) {
|
|
15
|
+
return (_jsxs("label", { className: "cedros-data-field", children: [label && _jsx("span", { className: "cedros-data-label", children: label }), _jsx("select", { className: cx("cedros-data-select", className), ...rest, children: children })] }));
|
|
16
|
+
}
|
|
17
|
+
export function JsonEditor({ label, className, ...rest }) {
|
|
18
|
+
return (_jsxs("label", { className: "cedros-data-field", children: [label && _jsx("span", { className: "cedros-data-label", children: label }), _jsx("textarea", { className: cx("cedros-data-textarea", className), ...rest })] }));
|
|
19
|
+
}
|
|
20
|
+
export function StatusNotice({ tone, message }) {
|
|
21
|
+
return _jsx("div", { className: cx("cedros-data-status", `cedros-data-status--${tone}`), children: message });
|
|
22
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type DataOperation = "bootstrap" | "export" | "import";
|
|
2
|
+
export type DataOperationStatus = "success" | "error";
|
|
3
|
+
export interface DataOpsHistoryRecord {
|
|
4
|
+
id: string;
|
|
5
|
+
operation: DataOperation;
|
|
6
|
+
status: DataOperationStatus;
|
|
7
|
+
message: string;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
artifact?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface DataOpsHistoryMutationResult {
|
|
12
|
+
records: DataOpsHistoryRecord[];
|
|
13
|
+
warning?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function readDataOpsHistory(): DataOpsHistoryRecord[];
|
|
16
|
+
export declare function appendDataOpsHistory(record: Omit<DataOpsHistoryRecord, "id" | "createdAt">): DataOpsHistoryMutationResult;
|
|
17
|
+
export declare function clearDataOpsHistory(): DataOpsHistoryMutationResult;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const STORAGE_KEY = "cedros-data:data-ops-history";
|
|
2
|
+
const MAX_RECORDS = 100;
|
|
3
|
+
const MAX_ARTIFACT_LENGTH = 2_000;
|
|
4
|
+
const STORAGE_WARNING = "Local history could not be saved in browser storage. The operation still completed.";
|
|
5
|
+
export function readDataOpsHistory() {
|
|
6
|
+
return readAllRecords();
|
|
7
|
+
}
|
|
8
|
+
export function appendDataOpsHistory(record) {
|
|
9
|
+
const entry = {
|
|
10
|
+
id: `${Date.now()}-${Math.random().toString(16).slice(2, 8)}`,
|
|
11
|
+
createdAt: new Date().toISOString(),
|
|
12
|
+
...record
|
|
13
|
+
};
|
|
14
|
+
entry.artifact = sanitizeArtifact(entry.artifact);
|
|
15
|
+
const records = [entry, ...readAllRecords()].slice(0, MAX_RECORDS);
|
|
16
|
+
return {
|
|
17
|
+
records,
|
|
18
|
+
warning: writeAllRecords(records)
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function clearDataOpsHistory() {
|
|
22
|
+
const records = [];
|
|
23
|
+
return {
|
|
24
|
+
records,
|
|
25
|
+
warning: writeAllRecords(records)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function readAllRecords() {
|
|
29
|
+
const storage = resolveLocalStorage();
|
|
30
|
+
if (!storage) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const raw = storage.getItem(STORAGE_KEY);
|
|
35
|
+
if (!raw) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
const parsed = JSON.parse(raw);
|
|
39
|
+
if (!Array.isArray(parsed)) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
const records = parsed.filter(isHistoryRecord).map(sanitizeHistoryRecord);
|
|
43
|
+
void writeAllRecords(records);
|
|
44
|
+
return records;
|
|
45
|
+
}
|
|
46
|
+
catch (_error) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function writeAllRecords(records) {
|
|
51
|
+
const storage = resolveLocalStorage();
|
|
52
|
+
if (!storage) {
|
|
53
|
+
return STORAGE_WARNING;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
storage.setItem(STORAGE_KEY, JSON.stringify(records));
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
catch (_error) {
|
|
60
|
+
return STORAGE_WARNING;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function resolveLocalStorage() {
|
|
64
|
+
if (typeof window === "undefined") {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
return window.localStorage;
|
|
69
|
+
}
|
|
70
|
+
catch (_error) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function isHistoryRecord(value) {
|
|
75
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const item = value;
|
|
79
|
+
return (typeof item.id === "string" &&
|
|
80
|
+
typeof item.operation === "string" &&
|
|
81
|
+
typeof item.status === "string" &&
|
|
82
|
+
typeof item.message === "string" &&
|
|
83
|
+
typeof item.createdAt === "string" &&
|
|
84
|
+
(item.artifact === undefined || typeof item.artifact === "string"));
|
|
85
|
+
}
|
|
86
|
+
function sanitizeHistoryRecord(record) {
|
|
87
|
+
return {
|
|
88
|
+
...record,
|
|
89
|
+
artifact: sanitizeArtifact(record.artifact)
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function sanitizeArtifact(artifact) {
|
|
93
|
+
if (!artifact) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
if (artifact.length <= MAX_ARTIFACT_LENGTH) {
|
|
97
|
+
return artifact;
|
|
98
|
+
}
|
|
99
|
+
return JSON.stringify({
|
|
100
|
+
note: "Legacy payload snapshot omitted from local history.",
|
|
101
|
+
originalLength: artifact.length
|
|
102
|
+
}, null, 2);
|
|
103
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export declare const Icons: {
|
|
3
|
+
pages: ReactNode;
|
|
4
|
+
navigation: ReactNode;
|
|
5
|
+
siteSettings: ReactNode;
|
|
6
|
+
media: ReactNode;
|
|
7
|
+
collections: ReactNode;
|
|
8
|
+
schemaDesigner: ReactNode;
|
|
9
|
+
contractVerify: ReactNode;
|
|
10
|
+
customData: ReactNode;
|
|
11
|
+
dataOps: ReactNode;
|
|
12
|
+
history: ReactNode;
|
|
13
|
+
tipping: ReactNode;
|
|
14
|
+
monetization: ReactNode;
|
|
15
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
function icon(path) {
|
|
3
|
+
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: path, stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
4
|
+
}
|
|
5
|
+
export const Icons = {
|
|
6
|
+
pages: icon("M4 5h16M4 12h16M4 19h16"),
|
|
7
|
+
navigation: icon("M3 6h18M3 12h10M3 18h14"),
|
|
8
|
+
siteSettings: icon("M12 3v4M12 17v4M3 12h4M17 12h4M5.6 5.6l2.8 2.8M15.6 15.6l2.8 2.8M18.4 5.6l-2.8 2.8M8.4 15.6l-2.8 2.8"),
|
|
9
|
+
media: icon("M4 5a1 1 0 011-1h14a1 1 0 011 1v14a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM8.5 10a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM20 15l-5-5L6 20"),
|
|
10
|
+
collections: icon("M5 4h14v4H5zM5 10h14v4H5zM5 16h14v4H5z"),
|
|
11
|
+
schemaDesigner: icon("M4 7h16M4 12h16M4 17h10M17 17h3"),
|
|
12
|
+
contractVerify: icon("M12 3l7 3v6c0 5-3.5 8-7 9-3.5-1-7-4-7-9V6l7-3z"),
|
|
13
|
+
customData: icon("M6 4h12M4 8h16M8 12h8M8 16h8M6 20h12"),
|
|
14
|
+
dataOps: icon("M4 4h16v16H4z M8 8h8 M8 12h8 M8 16h4"),
|
|
15
|
+
history: icon("M12 8v5l3 2M20 12a8 8 0 11-2.34-5.66"),
|
|
16
|
+
tipping: icon("M12 2a7 7 0 00-7 7c0 5.25 7 13 7 13s7-7.75 7-13a7 7 0 00-7-7z"),
|
|
17
|
+
monetization: icon("M12 1v22M17 5H9.5a3.5 3.5 0 000 7h5a3.5 3.5 0 010 7H6")
|
|
18
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type { AdminPlugin, AdminSectionConfig, AdminGroupConfig, AdminSectionProps, HostContext, PluginContext, PluginRegistry, PluginId, SectionId, QualifiedSectionId, PluginPermission } from "./types.js";
|
|
2
|
+
export { cedrosDataPlugin, dataPlugin } from "./plugin.js";
|
|
3
|
+
export { default as defaultDataPlugin } from "./plugin.js";
|
|
4
|
+
export { CEDROS_DATA_SECTIONS, CEDROS_DATA_SECTION_IDS, CEDROS_DATA_GROUPS } from "./sectionIds.js";
|
|
5
|
+
export { AdminButton, Card, TextInput, SelectInput, JsonEditor, StatusNotice } from "./components.js";
|
|
6
|
+
export { DataTable, type DataTableProps, type DataTableColumn } from "./primitives/DataTable.js";
|
|
7
|
+
export { Toolbar, SearchInput, FilterChips, type FilterChipItem } from "./primitives/Toolbar.js";
|
|
8
|
+
export { Pagination } from "./primitives/Pagination.js";
|
|
9
|
+
export { JsonCodeEditor } from "./primitives/JsonCodeEditor.js";
|
|
10
|
+
export { DiffViewer, type DiffBlock } from "./primitives/DiffViewer.js";
|
|
11
|
+
export { FormFieldRow, FieldTypeSelect, type FormFieldRowValue, type FieldTypeOption } from "./primitives/FormFieldRow.js";
|
|
12
|
+
export { ConfirmDialog } from "./primitives/ConfirmDialog.js";
|
|
13
|
+
export { ToastProvider, useToast, InlineAlert, type AlertTone } from "./primitives/alerts.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { cedrosDataPlugin, dataPlugin } from "./plugin.js";
|
|
2
|
+
export { default as defaultDataPlugin } from "./plugin.js";
|
|
3
|
+
export { CEDROS_DATA_SECTIONS, CEDROS_DATA_SECTION_IDS, CEDROS_DATA_GROUPS } from "./sectionIds.js";
|
|
4
|
+
export { AdminButton, Card, TextInput, SelectInput, JsonEditor, StatusNotice } from "./components.js";
|
|
5
|
+
export { DataTable } from "./primitives/DataTable.js";
|
|
6
|
+
export { Toolbar, SearchInput, FilterChips } from "./primitives/Toolbar.js";
|
|
7
|
+
export { Pagination } from "./primitives/Pagination.js";
|
|
8
|
+
export { JsonCodeEditor } from "./primitives/JsonCodeEditor.js";
|
|
9
|
+
export { DiffViewer } from "./primitives/DiffViewer.js";
|
|
10
|
+
export { FormFieldRow, FieldTypeSelect } from "./primitives/FormFieldRow.js";
|
|
11
|
+
export { ConfirmDialog } from "./primitives/ConfirmDialog.js";
|
|
12
|
+
export { ToastProvider, useToast, InlineAlert } from "./primitives/alerts.js";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type CedrosDataPermission = "data:pages:read" | "data:pages:write" | "data:navigation:read" | "data:navigation:write" | "data:settings:read" | "data:settings:write" | "data:collections:read" | "data:collections:write" | "data:schema:read" | "data:schema:write" | "data:contract:read" | "data:contract:write" | "data:custom:read" | "data:custom:write" | "data:media:read" | "data:media:write" | "data:history:read" | "data:ops:write" | "data:admin";
|
|
2
|
+
export declare const PERMISSION_MAP: Record<CedrosDataPermission, string[]>;
|
|
3
|
+
export declare function readPermissionForCollection(collectionName: string): CedrosDataPermission;
|
|
4
|
+
export declare function writePermissionForCollection(collectionName: string): CedrosDataPermission;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export const PERMISSION_MAP = {
|
|
2
|
+
"data:pages:read": ["owner", "admin", "data:pages:read", "data:admin"],
|
|
3
|
+
"data:pages:write": ["owner", "admin", "data:pages:write", "data:admin"],
|
|
4
|
+
"data:navigation:read": ["owner", "admin", "data:navigation:read", "data:admin"],
|
|
5
|
+
"data:navigation:write": ["owner", "admin", "data:navigation:write", "data:admin"],
|
|
6
|
+
"data:settings:read": ["owner", "admin", "data:settings:read", "data:admin"],
|
|
7
|
+
"data:settings:write": ["owner", "admin", "data:settings:write", "data:admin"],
|
|
8
|
+
"data:collections:read": ["owner", "admin", "data:collections:read", "data:admin"],
|
|
9
|
+
"data:collections:write": ["owner", "admin", "data:collections:write", "data:admin"],
|
|
10
|
+
"data:schema:read": ["owner", "admin", "data:schema:read", "data:admin"],
|
|
11
|
+
"data:schema:write": ["owner", "admin", "data:schema:write", "data:admin"],
|
|
12
|
+
"data:contract:read": ["owner", "admin", "data:contract:read", "data:admin"],
|
|
13
|
+
"data:contract:write": ["owner", "admin", "data:contract:write", "data:admin"],
|
|
14
|
+
"data:custom:read": ["owner", "admin", "data:custom:read", "data:admin"],
|
|
15
|
+
"data:custom:write": ["owner", "admin", "data:custom:write", "data:admin"],
|
|
16
|
+
"data:media:read": ["owner", "admin", "data:media:read", "data:admin"],
|
|
17
|
+
"data:media:write": ["owner", "admin", "data:media:write", "data:admin"],
|
|
18
|
+
"data:history:read": ["owner", "admin", "data:history:read", "data:admin"],
|
|
19
|
+
"data:ops:write": ["owner", "admin", "data:ops:write", "data:admin"],
|
|
20
|
+
"data:admin": ["owner", "admin", "data:admin"]
|
|
21
|
+
};
|
|
22
|
+
export function readPermissionForCollection(collectionName) {
|
|
23
|
+
switch (collectionName) {
|
|
24
|
+
case "pages":
|
|
25
|
+
return "data:pages:read";
|
|
26
|
+
case "navigation":
|
|
27
|
+
return "data:navigation:read";
|
|
28
|
+
case "site_settings":
|
|
29
|
+
return "data:settings:read";
|
|
30
|
+
default:
|
|
31
|
+
return "data:custom:read";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function writePermissionForCollection(collectionName) {
|
|
35
|
+
switch (collectionName) {
|
|
36
|
+
case "pages":
|
|
37
|
+
return "data:pages:write";
|
|
38
|
+
case "navigation":
|
|
39
|
+
return "data:navigation:write";
|
|
40
|
+
case "site_settings":
|
|
41
|
+
return "data:settings:write";
|
|
42
|
+
default:
|
|
43
|
+
return "data:custom:write";
|
|
44
|
+
}
|
|
45
|
+
}
|