smoodly 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +211 -0
  3. package/dist/admin/auth.d.ts +10 -0
  4. package/dist/admin/auth.js +28 -0
  5. package/dist/admin/client-ops.d.ts +2 -0
  6. package/dist/admin/client-ops.js +18 -0
  7. package/dist/admin/editor/EditorView.d.ts +7 -0
  8. package/dist/admin/editor/EditorView.js +391 -0
  9. package/dist/admin/editor/autosave.d.ts +31 -0
  10. package/dist/admin/editor/autosave.js +108 -0
  11. package/dist/admin/editor/protocol.d.ts +37 -0
  12. package/dist/admin/editor/protocol.js +1 -0
  13. package/dist/admin/forms/FieldWidget.d.ts +14 -0
  14. package/dist/admin/forms/FieldWidget.js +54 -0
  15. package/dist/admin/forms/tabs.d.ts +11 -0
  16. package/dist/admin/forms/tabs.js +20 -0
  17. package/dist/admin/index.d.ts +10 -0
  18. package/dist/admin/index.js +8 -0
  19. package/dist/admin/next/bridge.d.ts +1 -0
  20. package/dist/admin/next/bridge.js +66 -0
  21. package/dist/admin/next/create-admin.d.ts +18 -0
  22. package/dist/admin/next/create-admin.js +14 -0
  23. package/dist/admin/next/index.d.ts +6 -0
  24. package/dist/admin/next/index.js +3 -0
  25. package/dist/admin/next/op-handler.d.ts +11 -0
  26. package/dist/admin/next/op-handler.js +23 -0
  27. package/dist/admin/ops-impl.d.ts +20 -0
  28. package/dist/admin/ops-impl.js +189 -0
  29. package/dist/admin/ops.d.ts +52 -0
  30. package/dist/admin/ops.js +1 -0
  31. package/dist/admin/page-path.d.ts +1 -0
  32. package/dist/admin/page-path.js +6 -0
  33. package/dist/admin/richtext.d.ts +14 -0
  34. package/dist/admin/richtext.js +17 -0
  35. package/dist/admin/serialize.d.ts +40 -0
  36. package/dist/admin/serialize.js +49 -0
  37. package/dist/admin/shell/AdminApp.d.ts +9 -0
  38. package/dist/admin/shell/AdminApp.js +122 -0
  39. package/dist/admin/shell/EntryForm.d.ts +8 -0
  40. package/dist/admin/shell/EntryForm.js +96 -0
  41. package/dist/admin/shell/ListView.d.ts +15 -0
  42. package/dist/admin/shell/ListView.js +14 -0
  43. package/dist/admin/shell/LoginView.d.ts +5 -0
  44. package/dist/admin/shell/LoginView.js +47 -0
  45. package/dist/admin/shell/pages-list.d.ts +22 -0
  46. package/dist/admin/shell/pages-list.js +31 -0
  47. package/dist/admin/shell/session.d.ts +19 -0
  48. package/dist/admin/shell/session.js +37 -0
  49. package/dist/admin/supabase-auth.d.ts +7 -0
  50. package/dist/admin/supabase-auth.js +23 -0
  51. package/dist/admin/tree-ops.d.ts +19 -0
  52. package/dist/admin/tree-ops.js +112 -0
  53. package/dist/admin/validate.d.ts +10 -0
  54. package/dist/admin/validate.js +19 -0
  55. package/dist/client.d.ts +11 -0
  56. package/dist/client.js +19 -0
  57. package/dist/collections.d.ts +39 -0
  58. package/dist/collections.js +22 -0
  59. package/dist/config.d.ts +49 -0
  60. package/dist/config.js +54 -0
  61. package/dist/entry-store.d.ts +74 -0
  62. package/dist/entry-store.js +132 -0
  63. package/dist/env.d.ts +16 -0
  64. package/dist/env.js +22 -0
  65. package/dist/fields.d.ts +67 -0
  66. package/dist/fields.js +41 -0
  67. package/dist/index.d.ts +103 -0
  68. package/dist/index.js +29 -0
  69. package/dist/next/index.d.ts +3 -0
  70. package/dist/next/index.js +2 -0
  71. package/dist/next/meta.d.ts +10 -0
  72. package/dist/next/meta.js +37 -0
  73. package/dist/next/page-renderer.d.ts +36 -0
  74. package/dist/next/page-renderer.js +69 -0
  75. package/dist/page.d.ts +22 -0
  76. package/dist/page.js +35 -0
  77. package/dist/pairing.d.ts +33 -0
  78. package/dist/pairing.js +26 -0
  79. package/dist/ref-index.d.ts +19 -0
  80. package/dist/ref-index.js +0 -0
  81. package/dist/refs.d.ts +20 -0
  82. package/dist/refs.js +49 -0
  83. package/dist/render.d.ts +48 -0
  84. package/dist/render.js +41 -0
  85. package/dist/resolve.d.ts +22 -0
  86. package/dist/resolve.js +128 -0
  87. package/dist/revalidate.d.ts +8 -0
  88. package/dist/revalidate.js +34 -0
  89. package/dist/schema.d.ts +57 -0
  90. package/dist/schema.js +40 -0
  91. package/dist/section-wrapper.d.ts +11 -0
  92. package/dist/section-wrapper.js +19 -0
  93. package/dist/sql-space.d.ts +6 -0
  94. package/dist/sql-space.js +172 -0
  95. package/dist/sql.d.ts +3 -0
  96. package/dist/sql.js +162 -0
  97. package/dist/store.d.ts +73 -0
  98. package/dist/store.js +145 -0
  99. package/dist/supabase-entry-store.d.ts +26 -0
  100. package/dist/supabase-entry-store.js +191 -0
  101. package/dist/supabase-store.d.ts +26 -0
  102. package/dist/supabase-store.js +180 -0
  103. package/dist/zones.d.ts +31 -0
  104. package/dist/zones.js +23 -0
  105. package/package.json +50 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Laniakea Studio
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 ADDED
@@ -0,0 +1,211 @@
1
+ # smoodly (core package)
2
+
3
+ Define a component once in code — it's instantly a typed, editable,
4
+ AI-addressable building block your editors can compose, within the rails you
5
+ set. Content lives in Supabase; the admin mounts into your own Next.js app.
6
+
7
+ npx create-smoodly-app my-site
8
+
9
+ A self-hosted app needs three environment variables: `SMOODLY_URL`,
10
+ `SMOODLY_SECRET_KEY` and `SMOODLY_PUBLISHABLE_KEY`.
11
+ Source and docs: https://github.com/laniakea-studio/smoodly-cms
12
+
13
+ The registration runtime — the real implementation of the API designed in
14
+ Phase 0 (`sketches/phase0/` remains the readable spec; `docs/DESIGN.md`
15
+ holds the decisions). Test-driven; run with `npm test`.
16
+
17
+ Implemented:
18
+
19
+ - `f.*` field builders — inert, immutable, JSON-serializable descriptors
20
+ (ref thunks excepted); validation lives server/admin-side by design rule
21
+ - `smoodly.schema.section/element` — descriptor capture + the
22
+ fields-only-`localized()` rule enforced at construction
23
+ - `smoodly.section/element(schema, View)` — the typed pairing: returns a
24
+ real component carrying `.schema`; drift between schema and view is a
25
+ compile error at the pairing; style select defaults apply in hard-coded
26
+ JSX use; `Props<typeof schema>` infers required/optional/select types
27
+ - `z.sections([...]).max(n)` / `z.freeform` / `z.locked` — component
28
+ references in, names stored
29
+ - `smoodly.toolset` — plain values, element refs serialized to names
30
+ - `smoodly.collection` — descriptors + admin hints; ref thunks resolve
31
+ post-evaluation and infer the target entry shape
32
+ - `defineConfig` — registry name-uniqueness + the automatic standard-style
33
+ merge (`resolvedStyles`: section keys win, `styleDefaults: false|{omit}`)
34
+ - `<smoodly.Section>` — root wrapper: `as`, string styles → kebab-cased
35
+ data attributes, className/style passthrough
36
+
37
+ - `smoodly.schema.page` / `smoodly.page` / `<Zone>` / `renderPage` — the
38
+ pages runtime: policies normalized to plain data, tree nodes mapped to
39
+ registered components, fail-soft on unknown types
40
+ - `collectionSQL` / `coreTablesSQL` — the stable schema (DESIGN.md §3):
41
+ `coreTablesSQL` emits the once-ever core tables including `entries`
42
+ (`collection` + `fields`/`i18n` jsonb, `space_id` multi-tenancy with
43
+ a default space for self-hosted, `sort` for manual ordering, ONE
44
+ generic slug-uniqueness index across all spaces and collections);
45
+ `collectionSQL` emits only the disposable per-collection guardrails —
46
+ a published-only read view over `entries` (typed casts, snake_case
47
+ aliases, refs as plain id values) and partial btree indexes for
48
+ declared orderBy fields
49
+ - `PageStore` contract + `MemoryPageStore` — draft/publish semantics
50
+ (versions accumulate; publish moves a pointer; drafts never leak into
51
+ published reads). The contract is a reusable suite
52
+ (`test/page-store-contract.ts`); every adapter runs the same tests.
53
+ - `SupabasePageStore` — the Supabase adapter over pages/page_versions
54
+ (a server-side client from `createSmoodlyClient`: the service role in
55
+ self-host, a write key in the cloud). Contract-tested against a
56
+ real local stack: `supabase start` at the repo root (the generated
57
+ DDL is its one migration), then `npm run test:supabase`; plain
58
+ `npm test` skips it and never needs Docker.
59
+ - `resolveTree` — the batched ref-resolution pass: one fetch per
60
+ collection per round, ref-site depth modes (bare ref = entry only,
61
+ `.resolve(names)` = named refs one level, bare `.resolve()` = whole
62
+ chain), per-path cycle cutting with shallow copies, optional `maxDepth`
63
+ safety valve (default unlimited)
64
+
65
+ - `collectEntryRefs` / `collectTreeRefs` — the refs-index feeders: walk
66
+ an entry or a page tree against its schemas and list every outgoing
67
+ reference edge (derived, rebuildable, never the source of truth)
68
+ - Page saves feed the refs index — a page's rows are the UNION of its
69
+ draft and published trees' edges (source kind `page`), rewritten on
70
+ every `saveDraft`/`publish`, so cross-kind safe-delete holds while
71
+ either tree shows an entry. Publish integrity: `publish` walks the
72
+ draft tree directly and blocks on missing/unpublished entries.
73
+ Revalidation fan-out: `affectedPageSlugs` — reverse BFS over refs
74
+ (entry ← entry ← page, cycle-safe, optional `maxDepth`) returning
75
+ published slugs for the Next glue to `revalidateTag`. All wired via
76
+ store constructor options (`sections`, `entries`, shared
77
+ `MemoryRefIndex` in memory); the cross-store contract
78
+ (`test/page-refs-contract.ts`) runs against memory AND Supabase.
79
+ - `EntryStore` contract + `MemoryEntryStore` + `SupabaseEntryStore` —
80
+ collection-entry persistence (status-only versioning in v1): saves
81
+ rewrite the entry's refs rows, `referencesTo` answers the reverse
82
+ question, `getMany` is the batched read whose signature is
83
+ `resolveTree`'s `EntryFetcher` (with `{ status: "published" }` for the
84
+ published-route read — a post-publish demotion resolves as missing
85
+ instead of leaking draft fields — DESIGN.md §3 "Page edges in refs"), deletes are blocked while
86
+ referenced,
87
+ and `reorder` rewrites sort 1..N through the `smoodly_reorder` SQL
88
+ function (one statement, `updated_at` untouched). Same
89
+ reusable-contract pattern as PageStore; `npm run test:supabase` runs
90
+ both adapters against the local stack
91
+
92
+ - Proven end-to-end in `examples/site` — a real Next.js app wiring the
93
+ config + Supabase stores into routes: tag-cached resolved trees,
94
+ draft-mode preview, server-action writes with revalidation fan-out
95
+
96
+ - The space layer (spec 2026-09-04 §3): `space_id` on every content
97
+ table, `smoodly_current_space()`, the insert trigger, the row cap, the
98
+ three cloud-state tables (`editors`, `space_keys`, `space_caps`) and
99
+ the policy set. Service role bypasses it; a write key is scoped to its
100
+ space. Contract suites run in both modes; `test/isolation.supabase.test.ts`
101
+ proves isolation. Generated per-collection views are `security_invoker`,
102
+ so they read through the same policy set rather than their owner's
103
+ privileges. Live acceptance test in `test/rls.supabase.test.ts`. Note
104
+ for self-host: the anon key now reads published rows straight off the
105
+ base tables (`pages`, `entries` including the `i18n` overlay, a page's
106
+ published `page_versions` tree, `global_sections`), not only through
107
+ the generated views.
108
+
109
+ - Admin login: `supabaseAdminAuth` + `gateAdminOp` around the ops seam;
110
+ `LoginView` + `useAdminSession` in the shell; `createSmoodlyAdmin({ auth })`.
111
+ Env: `smoodlyEnv()` / `createSmoodlyClient()`.
112
+
113
+ - The admin, real and mounted: `smoodly/admin` (the AdminOps seam —
114
+ `createAdminOps`/`runAdminOp` server-side over the stores,
115
+ `makeClientOps` client-side forwarding `{ path, args }` through one
116
+ server action; typed `OpResult`, never throws — `serializeAdminConfig`
117
+ turns the registry into the admin's nav/forms; `validateFields` +
118
+ tree-ops for pure canvas mutations), `smoodly/admin/next`
119
+ (`createSmoodlyAdmin` binds registry + op action into `AdminPage`/
120
+ `AdminLayout` for the app's `[[...segments]]` route group;
121
+ `createAdminOpHandler` is the one server action the client ops call),
122
+ and `smoodly/bridge` (`EditorBridge`, the only piece that ships to the
123
+ SITE side — runs inside the draft-route iframe, reports node/zone
124
+ geometry, forwards select/hover, executes refresh/scroll). Shell
125
+ (registry-derived nav, list views, segment-based internal routing),
126
+ entry form (widgets by field type, validation errors surfaced inline,
127
+ save/publish/unpublish/delete with the safe-delete guard's message
128
+ shown verbatim), and the editor view (dark instrument around a bright
129
+ canvas — the iframe is the site's own draft route, pixel-identical by
130
+ construction; every gesture mutates client tree state through pure
131
+ ops, a debounced `saveDraft` persists, and the bridge refreshes the
132
+ iframe on the save ack — refresh-on-change preview, not a live DOM
133
+ sync) are all real, in-house. The autosave itself is a framework-free
134
+ controller (`createDraftAutosave`: debounce, doubling capped retry,
135
+ pre-publish `flush`) wrapped by `useDraftAutosave`; the controller is
136
+ unit-tested with fake timers, the hook only for its initial snapshot. Proven end-to-end in
137
+ `examples/site/app/(smoodly)/admin` against the local Supabase stack —
138
+ see its README's acceptance walk.
139
+
140
+ - `smoodly/next` — `createPageRenderer`, the site-route seam: draft
141
+ branch, tag-cached published read, ref resolution, 404 and editor
142
+ bridge behind one `renderSmoodlyPage(Page, { slug?, searchParams? })`,
143
+ plus `smoodlyMetadata` for `generateMetadata`. Both await Next's
144
+ `connection()` before reading the stores, so a fixed-slug route is
145
+ request-rendered without a `force-dynamic` export and `next build`
146
+ never reaches the database (`test/next-page-renderer.test.tsx`). Bound
147
+ once by the app, like `createAdminOpHandler`. Page schemas may declare a fixed `slug`
148
+ (a singleton owned by one route file); the admin reads it to filter
149
+ New Page and protect the record. `defineConfig({ homePageSlug })`
150
+ (default `"home"`) names the slug the site root serves; the admin's
151
+ `pagePath()` maps it to `/` — canvas URL and Pages list — and every
152
+ other slug to `/<slug>`.
153
+
154
+ - Locked zones are usable: `fillLockedZones` (a pure tree op) materializes
155
+ the one node of every empty locked zone from the component's `sample`
156
+ when the editor loads a draft, and the change rides the normal debounced
157
+ save. No gesture can insert into or remove from a locked zone, so this is
158
+ the only way the node gets there — it also covers pages that existed
159
+ before a locked zone was added to their schema.
160
+
161
+ Not yet: richtext components (TipTap; `plainToRichtext`/
162
+ `richtextToPlain` exist but no rich editor widget), drag-and-drop
163
+ reordering (move-up/move-down buttons only), undo/redo, sample/
164
+ placeholder content marking, editor localization (the
165
+ locale switcher — examples/site is single-locale so it stays hidden).
166
+
167
+ ## Releasing
168
+
169
+ Both packages move together. From the repo root:
170
+
171
+ npm version 0.0.2 -w smoodly -w create-smoodly-app --no-git-tag-version
172
+ npm run build
173
+ npm publish -w smoodly --access public
174
+ npm publish -w create-smoodly-app --access public
175
+ git commit -am "release: 0.0.2" && git tag v0.0.2 && git push --tags origin main
176
+
177
+ `prepublishOnly` rebuilds each package; the CLI's template is regenerated from
178
+ `examples/site` in that build, so the template always matches the example
179
+ site at the tagged commit.
180
+
181
+ ## Follow-ups (logged 2026-09-04)
182
+
183
+ - Cloud-ready foundation — done, including the self-host init
184
+ (`create-smoodly-app`, spec 2026-09-04). Remaining from the spec: none
185
+ in the package. The two-mode contract suites and the isolation suite
186
+ run in CI in the `smoodly-supabase` job (a local stack on the runner) — if that job
187
+ proves flaky, the fallback is a Postgres service container applying
188
+ the one migration.
189
+ - Once the policy-based published read exists, decide whether it becomes the canonical headless read path and the generated per-collection views turn optional (cloud has no views).
190
+ - Add an index on `pages(published_version_id)`: the `page_versions` read policy's `exists` probe runs on every anon/read-key read and has no supporting index.
191
+ - A malformed `space_id`/`key_id` claim raises `22P02` inside the policy: fails closed but surfaces as a 500. Only reachable from a validly-signed key, so it is a cloud key-minting guard, not a request-path one.
192
+ - Authorization trusts the JWT claims alone; `smoodly_space_key_revoked()` checks only `revoked_at`. When cloud key-minting exists, make it a full key check (the claims must match the `space_keys` row).
193
+ - `isEditor` reads `editors` with no space filter: under a cloud write key RLS scopes it, but a service-role client bypasses RLS and would admit an editor of any space. Harmless in self-host (one space); never use a service-role client against the shared content project.
194
+ - The `sb_secret_` branch of `clientOptions` is unit-tested for shape only; the local stack uses legacy JWT keys, so a new-format secret key has not been exercised live against PostgREST and the GoTrue admin API.
195
+ - The migration is not re-appliable to a pre-rename (`project_id`) database: `create table if not exists` leaves it untouched and the `space_id` policies then fail. No installs predate the rename; fold into DESIGN.md OQ #2 (schema upgrades).
196
+ - `examples/site/lib/smoodly/index.ts` should carry `import "server-only"` now that it also exports `publicAuth` — a client-side import would fail loudly at build instead of at runtime.
197
+ - `exports` has no `"./package.json"` entry, so tooling that reads a dependency's `package.json` through the exports map cannot. One line to add when something needs it.
198
+ - `src/ref-index.ts` contains two raw NUL bytes used as key separators, which makes git show the file as binary in diffs. Replacing them with `\0` escapes would make it diffable again.
199
+
200
+ ## Follow-ups (logged 2026-09-03)
201
+
202
+ - Empty locked zones on the site side: `renderPage` renders nothing for a locked zone with no node. A page that predates the zone shows no prefooter on the published site until an editor opens it (the editor fills and saves). Options: render the component from `sample` when the zone is empty, or fill on `createPage`/schema load server-side. Fine for now — seeded and admin-created pages both arrive filled.
203
+
204
+ - Editor canvas URL mapping: now `homePageSlug` config (done 2026-09-03); remaining follow-up is a registration-declared `path` for fixed pages at arbitrary paths.
205
+ - `serialize.ts` structural cast duplicates `PageSchema`; tighten `Registered.schema` so the cast goes.
206
+ - Pages list: no re-entrancy guard on a fast double-click of "New page" / a not-created row, and the row `onClick` promise is unhandled (a transport throw is an unhandled rejection); fix both together with a `.catch` → alert.
207
+ - `pagesRows`: pin with a test the case of a record at a fixed slug whose `template` is an open registration (self-healing today: `/` 404s, `/<slug>` serves it, delete is allowed, then the singleton row reappears).
208
+ - Catch-all `templates` map should filter to open registrations to state its intent (unreachable today).
209
+ - The no-database build is the real proof that routes never prerender against the stores; the unit test only stubs `next/server`. Script it (`SUPABASE_URL=http://127.0.0.1:9 npx next build` in `examples/site`, expect `ƒ /`) and add it to CI, which today runs only the package's tests and typecheck — so a regression is caught when Next changes `connection()` semantics.
210
+ - Next acceptance walk: start from a database with no `home` record so the "not created" singleton row is exercised.
211
+ - `EditorView` split, remaining seams (autosave extracted 2026-09-03): pull the canvas overlay and the insert picker into components when the dnd-kit outline work starts; the sidebar form stays put until inline editing lands (DESIGN.md §3 puts TipTap in the canvas, not the sidebar).
@@ -0,0 +1,10 @@
1
+ import type { AdminOpCall, OpResult } from "./ops.ts";
2
+ export type AdminAuth = {
3
+ /** The user behind a session token, or null when the auth project rejects it. */
4
+ verify(token: string): Promise<{
5
+ userId: string;
6
+ } | null>;
7
+ /** Does `editors` hold this user for the current space? */
8
+ isEditor(userId: string): Promise<boolean>;
9
+ };
10
+ export declare function gateAdminOp(auth: AdminAuth, call: AdminOpCall, run: (call: AdminOpCall) => Promise<OpResult<unknown>>): Promise<OpResult<unknown>>;
@@ -0,0 +1,28 @@
1
+ const unauthorized = (message) => ({ ok: false, code: "unauthorized", message });
2
+ export async function gateAdminOp(auth, call, run) {
3
+ const token = typeof call === "object" && call !== null && typeof call.token === "string"
4
+ ? call.token
5
+ : null;
6
+ if (!token)
7
+ return unauthorized("smoodly: sign in to use the admin.");
8
+ let session;
9
+ let editor;
10
+ try {
11
+ session = await auth.verify(token);
12
+ if (!session)
13
+ return unauthorized("smoodly: your session has expired — sign in again.");
14
+ editor = await auth.isEditor(session.userId);
15
+ }
16
+ catch (e) {
17
+ console.error("smoodly: auth check failed", e);
18
+ return {
19
+ ok: false,
20
+ code: "internal",
21
+ message: "smoodly: could not check your session.",
22
+ };
23
+ }
24
+ if (!editor) {
25
+ return unauthorized("smoodly: this account is not an editor of this site.");
26
+ }
27
+ return run(call);
28
+ }
@@ -0,0 +1,2 @@
1
+ import type { AdminOpAction, AdminOps } from "./ops.ts";
2
+ export declare function makeClientOps(action: AdminOpAction, token?: () => string | null): AdminOps;
@@ -0,0 +1,18 @@
1
+ const GROUPS = {
2
+ pages: ["list", "get", "create", "saveDraft", "publish", "delete"],
3
+ entries: ["list", "get", "create", "save", "setStatus", "delete"],
4
+ preview: ["enable", "disable"],
5
+ };
6
+ export function makeClientOps(action, token) {
7
+ const out = {};
8
+ for (const [group, methods] of Object.entries(GROUPS)) {
9
+ out[group] = {};
10
+ for (const method of methods) {
11
+ out[group][method] = (...args) => {
12
+ const t = token?.() ?? null;
13
+ return action({ path: `${group}.${method}`, args, ...(t ? { token: t } : {}) });
14
+ };
15
+ }
16
+ }
17
+ return out;
18
+ }
@@ -0,0 +1,7 @@
1
+ import type { AdminOps } from "../ops.ts";
2
+ import type { AdminRegistry } from "../serialize.ts";
3
+ export declare function EditorView({ ops, registry, slug }: {
4
+ ops: AdminOps;
5
+ registry: AdminRegistry;
6
+ slug: string;
7
+ }): import("react").JSX.Element;