smoodly 0.0.44 → 0.0.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/addresses.d.ts +6 -7
- package/dist/addresses.js +6 -4
- package/dist/admin/PublicSans-LICENSE.txt +93 -0
- package/dist/admin/auth-config.d.ts +6 -0
- package/dist/admin/auth-config.js +1 -0
- package/dist/admin/browser/fetch-op.d.ts +10 -0
- package/dist/admin/browser/fetch-op.js +44 -0
- package/dist/admin/browser/main.d.ts +1 -0
- package/dist/admin/browser/main.js +21 -0
- package/dist/admin/custom-section.d.ts +1 -1
- package/dist/admin/drift.d.ts +1 -1
- package/dist/admin/editor/EditorView.js +14 -14
- package/dist/admin/editor/autosave.js +1 -1
- package/dist/admin/editor/canvas-address.d.ts +2 -0
- package/dist/admin/editor/canvas-address.js +8 -0
- package/dist/admin/editor/overlay-geometry.d.ts +1 -1
- package/dist/admin/editor/protocol.d.ts +18 -4
- package/dist/admin/editor/protocol.js +15 -1
- package/dist/admin/editor/useCanvasBridge.d.ts +1 -1
- package/dist/admin/editor/useCanvasBridge.js +5 -6
- package/dist/admin/editor/useInlineSession.js +4 -2
- package/dist/admin/files.d.ts +11 -0
- package/dist/admin/files.generated.js +2 -0
- package/dist/admin/files.js +8 -0
- package/dist/admin/forms/useRefOptions.d.ts +2 -2
- package/dist/admin/forms/useRefOptions.js +2 -2
- package/dist/admin/forms/useRichtextEditor.js +4 -4
- package/dist/admin/handler.d.ts +40 -0
- package/dist/admin/handler.js +151 -0
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/ops-impl.d.ts +7 -1
- package/dist/admin/ops-impl.js +110 -33
- package/dist/admin/ops.d.ts +3 -2
- package/dist/admin/page-data.d.ts +21 -0
- package/dist/admin/page-data.js +4 -0
- package/dist/admin/page.d.ts +6 -0
- package/dist/admin/page.js +35 -0
- package/dist/admin/serialize.js +7 -7
- package/dist/admin/shell/AdminApp.js +2 -2
- package/dist/admin/shell/EntryForm.js +1 -1
- package/dist/admin/shell/router.d.ts +3 -16
- package/dist/admin/shell/router.js +3 -39
- package/dist/admin/shell/session.d.ts +2 -5
- package/dist/admin/supabase-auth.js +1 -1
- package/dist/admin/tree-ops.d.ts +1 -1
- package/dist/admin/ui/fonts.d.ts +12 -0
- package/dist/admin/ui/fonts.js +22 -0
- package/dist/admin/ui/theme.d.ts +13 -4
- package/dist/admin/ui/theme.js +28 -157
- package/dist/assemble.d.ts +60 -0
- package/dist/assemble.js +359 -0
- package/dist/astro/cache.d.ts +34 -0
- package/dist/astro/cache.js +69 -0
- package/dist/astro/components/Custom.astro +21 -0
- package/dist/astro/components/Guarded.astro +24 -0
- package/dist/astro/components/Head.astro +26 -0
- package/dist/astro/components/Image.astro +20 -0
- package/dist/astro/components/RichText.astro +23 -0
- package/dist/astro/components/Section.astro +20 -0
- package/dist/astro/components/Video.astro +5 -0
- package/dist/astro/components/Zone.astro +19 -0
- package/dist/astro/components.d.ts +6 -0
- package/dist/astro/components.js +7 -0
- package/dist/astro/content-loaders.d.ts +40 -0
- package/dist/astro/content-loaders.js +100 -0
- package/dist/astro/content.d.ts +9 -0
- package/dist/astro/content.js +17 -0
- package/dist/astro/custom.d.ts +1 -0
- package/dist/astro/custom.js +3 -0
- package/dist/astro/defaults.d.ts +20 -0
- package/dist/astro/defaults.js +21 -0
- package/dist/astro/env-files.d.ts +1 -0
- package/dist/astro/env-files.js +21 -0
- package/dist/astro/head.d.ts +13 -0
- package/dist/astro/head.js +19 -0
- package/dist/astro/image.d.ts +27 -0
- package/dist/astro/image.js +29 -0
- package/dist/astro/index.d.ts +43 -0
- package/dist/astro/index.js +111 -0
- package/dist/astro/locale-paths.d.ts +30 -0
- package/dist/astro/locale-paths.js +38 -0
- package/dist/astro/middleware.d.ts +1 -0
- package/dist/astro/middleware.js +83 -0
- package/dist/astro/preview-document.d.ts +1 -0
- package/dist/astro/preview-document.js +18 -0
- package/dist/astro/preview.d.ts +10 -0
- package/dist/astro/preview.js +72 -0
- package/dist/astro/richtext.d.ts +15 -0
- package/dist/astro/richtext.js +78 -0
- package/dist/astro/routes/admin.d.ts +3 -0
- package/dist/astro/routes/admin.js +41 -0
- package/dist/astro/routes/page.astro +17 -0
- package/dist/astro/routes/preview.astro +11 -0
- package/dist/astro/runtime.d.ts +32 -0
- package/dist/astro/runtime.js +46 -0
- package/dist/astro/scope.d.ts +45 -0
- package/dist/astro/scope.js +103 -0
- package/dist/astro/server.d.ts +14 -0
- package/dist/astro/server.js +45 -0
- package/dist/astro/shell.d.ts +67 -0
- package/dist/astro/shell.js +63 -0
- package/dist/astro/style.d.ts +1 -0
- package/dist/astro/style.js +11 -0
- package/dist/astro/views-check.d.ts +3 -0
- package/dist/astro/views-check.js +19 -0
- package/dist/astro/zone.d.ts +39 -0
- package/dist/astro/zone.js +33 -0
- package/dist/bind.d.ts +19 -0
- package/dist/bind.js +34 -0
- package/dist/bridge/bridge.d.ts +9 -0
- package/dist/bridge/bridge.js +107 -0
- package/dist/bridge/entry.d.ts +1 -0
- package/dist/bridge/entry.js +5 -0
- package/dist/bridge/preview-pill.d.ts +18 -0
- package/dist/bridge/preview-pill.js +115 -0
- package/dist/cli/import.d.ts +3 -0
- package/dist/cli/import.js +10 -1
- package/dist/cli/load-collections.js +4 -3
- package/dist/cli/load-config.d.ts +4 -0
- package/dist/cli/load-config.js +26 -0
- package/dist/cli/main.d.ts +7 -1
- package/dist/cli/main.js +32 -7
- package/dist/cli/migrate.d.ts +3 -0
- package/dist/cli/migrate.js +8 -1
- package/dist/cli/rebuild.d.ts +23 -0
- package/dist/cli/rebuild.js +22 -0
- package/dist/cli/space-io.d.ts +15 -1
- package/dist/cli/space-io.js +14 -0
- package/dist/client.js +5 -0
- package/dist/config.d.ts +28 -20
- package/dist/config.js +71 -75
- package/dist/content/content-api.d.ts +1 -1
- package/dist/content/node.d.ts +2 -3
- package/dist/content/node.js +2 -1
- package/dist/crash-message.d.ts +1 -0
- package/dist/crash-message.js +8 -0
- package/dist/css.d.ts +4 -0
- package/dist/css.js +5 -2
- package/dist/custom/CustomSection.d.ts +1 -1
- package/dist/custom/CustomSection.js +4 -8
- package/dist/custom/css.js +2 -2
- package/dist/custom/index.d.ts +6 -10
- package/dist/custom/index.js +1 -2
- package/dist/custom/layout.d.ts +5 -0
- package/dist/custom/layout.js +14 -0
- package/dist/entry-page.d.ts +3 -18
- package/dist/env.js +1 -1
- package/dist/fields.js +2 -1
- package/dist/global-id.js +2 -2
- package/dist/global.d.ts +5 -2
- package/dist/global.js +8 -5
- package/dist/image/next-config.d.ts +3 -0
- package/dist/image/next-config.js +12 -7
- package/dist/image/remote-pattern.d.ts +1 -0
- package/dist/image/remote-pattern.js +5 -0
- package/dist/index.d.ts +15 -17
- package/dist/index.js +10 -19
- package/dist/marks.d.ts +7 -0
- package/dist/marks.js +7 -0
- package/dist/next/admin-route.d.ts +7 -0
- package/dist/next/admin-route.js +28 -0
- package/dist/next/bridge.d.ts +1 -0
- package/dist/next/bridge.js +26 -0
- package/dist/next/canvas-boundary.d.ts +1 -1
- package/dist/next/canvas-slot.d.ts +1 -0
- package/dist/next/canvas-slot.js +4 -3
- package/dist/next/index.d.ts +2 -0
- package/dist/next/index.js +1 -0
- package/dist/next/page-renderer.d.ts +5 -1
- package/dist/next/page-renderer.js +19 -33
- package/dist/next/preview-pill-define.d.ts +1 -0
- package/dist/next/preview-pill-define.js +10 -0
- package/dist/next/preview-pill.d.ts +0 -3
- package/dist/next/preview-pill.js +7 -50
- package/dist/next/queries.d.ts +3 -28
- package/dist/next/queries.js +3 -90
- package/dist/next/reads.d.ts +2 -0
- package/dist/next/reads.js +18 -0
- package/dist/page-context.d.ts +89 -0
- package/dist/page-context.js +8 -0
- package/dist/pairing.d.ts +2 -22
- package/dist/project.d.ts +69 -0
- package/dist/project.js +210 -0
- package/dist/published.d.ts +96 -0
- package/dist/published.js +125 -0
- package/dist/react.d.ts +61 -0
- package/dist/react.js +17 -0
- package/dist/read-shapes.d.ts +1 -1
- package/dist/reads.d.ts +80 -0
- package/dist/reads.js +142 -0
- package/dist/refs.d.ts +1 -1
- package/dist/registration.d.ts +8 -0
- package/dist/registration.js +17 -0
- package/dist/render.d.ts +5 -83
- package/dist/render.js +5 -10
- package/dist/resolve.d.ts +1 -1
- package/dist/richtext/index.d.ts +6 -2
- package/dist/richtext/index.js +3 -2
- package/dist/richtext-href.d.ts +4 -0
- package/dist/richtext-href.js +17 -0
- package/dist/richtext-render.js +1 -14
- package/dist/schema-version.d.ts +1 -1
- package/dist/schema-version.js +1 -1
- package/dist/section-crash.d.ts +2 -2
- package/dist/section-crash.js +2 -7
- package/dist/section-wrapper.d.ts +3 -10
- package/dist/section-wrapper.js +3 -2
- package/dist/site.d.ts +38 -14
- package/dist/site.js +159 -286
- package/dist/smd-key.d.ts +9 -0
- package/dist/smd-key.js +1 -0
- package/dist/sql-space.d.ts +4 -0
- package/dist/sql-space.js +10 -5
- package/dist/sql.d.ts +2 -0
- package/dist/sql.js +117 -0
- package/dist/store.d.ts +1 -1
- package/dist/supabase-published-store.d.ts +35 -0
- package/dist/supabase-published-store.js +215 -0
- package/dist/supabase-store.d.ts +1 -1
- package/dist/toolset.d.ts +2 -4
- package/dist/toolset.js +6 -4
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/view-props.d.ts +40 -0
- package/dist/view-props.js +1 -0
- package/dist/views.d.ts +36 -0
- package/dist/views.js +52 -0
- package/dist/zones.d.ts +7 -7
- package/dist/zones.js +7 -6
- package/migrations/20261007120000_smoodly_v8.sql +154 -0
- package/migrations/20261007200000_smoodly_v9.sql +30 -0
- package/package.json +46 -19
- package/dist/admin/next/bridge.d.ts +0 -2
- package/dist/admin/next/bridge.js +0 -129
- package/dist/admin/next/create-admin.d.ts +0 -19
- package/dist/admin/next/create-admin.js +0 -44
- package/dist/admin/next/index.d.ts +0 -8
- package/dist/admin/next/index.js +0 -8
- package/dist/admin/next/op-handler.d.ts +0 -18
- package/dist/admin/next/op-handler.js +0 -31
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// The delivery model's store (spec 2026-10-07 delivery model §1, §5): the
|
|
2
|
+
// finished documents Publish writes, one row per page, entry or global per
|
|
3
|
+
// locale, read in one lookup. Derived from the editing tables and
|
|
4
|
+
// rebuildable; the memory adapter is two maps with the matcher in code, the
|
|
5
|
+
// Supabase one (supabase-published-store.ts) reads published_pages,
|
|
6
|
+
// published_entries, the published_routes view and smoodly_list_published.
|
|
7
|
+
import { matchesWhere } from "./entry-store.js";
|
|
8
|
+
const keyOf = (id, locale) => `${id}:${locale}`;
|
|
9
|
+
const asc = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
10
|
+
/** smoodly_list_published's ORDER BY: manual by sort, else the built-in time or
|
|
11
|
+
* the field, absent, null and empty last in both directions; ties by creation, then id. */
|
|
12
|
+
function compareRows(order) {
|
|
13
|
+
return (a, b) => {
|
|
14
|
+
if (order === "manual") {
|
|
15
|
+
const d = (a.sort ?? Infinity) - (b.sort ?? Infinity);
|
|
16
|
+
if (d !== 0 && !Number.isNaN(d))
|
|
17
|
+
return d;
|
|
18
|
+
}
|
|
19
|
+
else if (order) {
|
|
20
|
+
const value = (r) => {
|
|
21
|
+
const v = order.by === "createdAt" ? r.createdAt : order.by === "updatedAt" ? r.updatedAt : r.fields[order.by];
|
|
22
|
+
return v === "" ? null : v;
|
|
23
|
+
};
|
|
24
|
+
const va = value(a), vb = value(b);
|
|
25
|
+
if (va == null && vb != null)
|
|
26
|
+
return 1;
|
|
27
|
+
if (vb == null && va != null)
|
|
28
|
+
return -1;
|
|
29
|
+
if (va != null && vb != null) {
|
|
30
|
+
const d = typeof va === "number" && typeof vb === "number" ? va - vb : String(va).localeCompare(String(vb));
|
|
31
|
+
if (d !== 0)
|
|
32
|
+
return order.direction === "asc" ? d : -d;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return asc(a.createdAt, b.createdAt) || asc(a.id, b.id);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** An address holds one row per table, as the SQL's unique (space, locale, path) index says: a row of another id
|
|
39
|
+
* left at an incoming address (a stale row a capped rename did not reach) goes before the write. */
|
|
40
|
+
function vacate(map, rows) {
|
|
41
|
+
const incoming = new Map(rows.filter((r) => r.path !== null).map((r) => [`${r.locale}\u0000${r.path}`, r.id]));
|
|
42
|
+
for (const [k, row] of map) {
|
|
43
|
+
const holder = row.path === null ? undefined : incoming.get(`${row.locale}\u0000${row.path}`);
|
|
44
|
+
if (holder !== undefined && holder !== row.id)
|
|
45
|
+
map.delete(k);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export class MemoryPublishedStore {
|
|
49
|
+
constructor() {
|
|
50
|
+
this.pages = new Map();
|
|
51
|
+
this.entryRows = new Map();
|
|
52
|
+
}
|
|
53
|
+
async putPages(rows) {
|
|
54
|
+
vacate(this.pages, rows);
|
|
55
|
+
for (const row of rows)
|
|
56
|
+
this.pages.set(keyOf(row.id, row.locale), structuredClone(row));
|
|
57
|
+
}
|
|
58
|
+
async putEntries(rows) {
|
|
59
|
+
vacate(this.entryRows, rows);
|
|
60
|
+
for (const row of rows)
|
|
61
|
+
this.entryRows.set(keyOf(row.id, row.locale), structuredClone(row));
|
|
62
|
+
}
|
|
63
|
+
async remove(keys) {
|
|
64
|
+
for (const key of keys) {
|
|
65
|
+
const map = key.kind === "page" ? this.pages : this.entryRows;
|
|
66
|
+
for (const [k, row] of map)
|
|
67
|
+
if (row.id === key.id && (key.locale === undefined || row.locale === key.locale))
|
|
68
|
+
map.delete(k);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async byAddress(locale, path, globals) {
|
|
72
|
+
const found = () => {
|
|
73
|
+
for (const row of this.pages.values()) {
|
|
74
|
+
if (row.locale === locale && row.path === path && row.hasPage)
|
|
75
|
+
return { kind: "page", row: structuredClone(row) };
|
|
76
|
+
}
|
|
77
|
+
for (const row of this.entryRows.values()) {
|
|
78
|
+
if (row.locale === locale && row.path === path)
|
|
79
|
+
return { kind: "entry", row: structuredClone(row) };
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
};
|
|
83
|
+
const route = found();
|
|
84
|
+
if (!route || !globals)
|
|
85
|
+
return route;
|
|
86
|
+
const wanted = new Set(globals);
|
|
87
|
+
route.globals = structuredClone([...this.entryRows.values()].filter((r) => r.locale === locale && wanted.has(r.collection)));
|
|
88
|
+
return route;
|
|
89
|
+
}
|
|
90
|
+
async page(id, locale) {
|
|
91
|
+
const row = this.pages.get(keyOf(id, locale));
|
|
92
|
+
return row ? structuredClone(row) : null;
|
|
93
|
+
}
|
|
94
|
+
async entry(id, locale) {
|
|
95
|
+
const row = this.entryRows.get(keyOf(id, locale));
|
|
96
|
+
return row ? structuredClone(row) : null;
|
|
97
|
+
}
|
|
98
|
+
async entries(collection, q) {
|
|
99
|
+
const wanted = q.ids ? new Set(q.ids) : null;
|
|
100
|
+
const rows = [...this.entryRows.values()].filter((r) => r.collection === collection &&
|
|
101
|
+
r.locale === q.locale &&
|
|
102
|
+
(q.parent === undefined || r.parentId === q.parent) &&
|
|
103
|
+
(!wanted || wanted.has(r.id)) &&
|
|
104
|
+
(!q.where || matchesWhere(r.fields, q.where)));
|
|
105
|
+
rows.sort(compareRows(q.order));
|
|
106
|
+
return structuredClone(typeof q.limit === "number" ? rows.slice(0, q.limit) : rows);
|
|
107
|
+
}
|
|
108
|
+
async tree(locale) {
|
|
109
|
+
return [...this.pages.values()]
|
|
110
|
+
.filter((r) => r.locale === locale)
|
|
111
|
+
.sort((a, b) => {
|
|
112
|
+
const d = (a.sort ?? Infinity) - (b.sort ?? Infinity);
|
|
113
|
+
return (d !== 0 && !Number.isNaN(d) ? d : 0) || asc(a.publishedAt, b.publishedAt) || asc(a.id, b.id);
|
|
114
|
+
})
|
|
115
|
+
.map(({ id, parentId, sort, path, title, hasPage, linkFields, model, stale }) => structuredClone({ id, parentId, sort, path, title, hasPage, linkFields, model, stale }));
|
|
116
|
+
}
|
|
117
|
+
async markStale(keys) {
|
|
118
|
+
for (const key of keys) {
|
|
119
|
+
const map = key.kind === "page" ? this.pages : this.entryRows;
|
|
120
|
+
for (const row of map.values())
|
|
121
|
+
if (row.id === key.id && (key.locale === undefined || row.locale === key.locale))
|
|
122
|
+
row.stale = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { element, section } from "./pairing.tsx";
|
|
2
|
+
import { page } from "./render.tsx";
|
|
3
|
+
import { entryPage } from "./entry-page.tsx";
|
|
4
|
+
import { Section } from "./section-wrapper.tsx";
|
|
5
|
+
export * from "./index.ts";
|
|
6
|
+
export { element, entryPage, page, section, Section };
|
|
7
|
+
export type { PairedComponent } from "./pairing.tsx";
|
|
8
|
+
export { Zone, renderPage } from "./render.tsx";
|
|
9
|
+
export type { PairedPage, PageViewProps, Registry } from "./render.tsx";
|
|
10
|
+
export type { PairedEntryPage } from "./entry-page.tsx";
|
|
11
|
+
export { RichText, defaultRichTextImage } from "./richtext-render.tsx";
|
|
12
|
+
export type { RichTextProps, RichTextElement, RichTextImageProps } from "./richtext-render.tsx";
|
|
13
|
+
export { defineViews } from "./views.ts";
|
|
14
|
+
export type { Views, ViewsInput } from "./views.ts";
|
|
15
|
+
export { createCustomSection } from "./custom/CustomSection.tsx";
|
|
16
|
+
export type { CustomOptions, CustomSectionProps, CustomSectionComponent } from "./custom/CustomSection.tsx";
|
|
17
|
+
/** The root's namespace plus the React pairing: `smoodly.section(schema, View)`, `<smoodly.Section>`. */
|
|
18
|
+
export declare const smoodly: {
|
|
19
|
+
section: typeof section;
|
|
20
|
+
element: typeof element;
|
|
21
|
+
page: typeof page;
|
|
22
|
+
entryPage: typeof entryPage;
|
|
23
|
+
Section: typeof Section;
|
|
24
|
+
schema: {
|
|
25
|
+
page: typeof import("./page.ts").pageSchema;
|
|
26
|
+
section<F extends import("./schema.ts").BuilderMap = {}, S extends import("./schema.ts").BuilderMap = {}>(input: {
|
|
27
|
+
name: string;
|
|
28
|
+
title: string;
|
|
29
|
+
icon?: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
fields?: F | undefined;
|
|
32
|
+
settings?: S | undefined;
|
|
33
|
+
meta?: never;
|
|
34
|
+
styles?: false | {
|
|
35
|
+
omit: string[];
|
|
36
|
+
};
|
|
37
|
+
css?: false;
|
|
38
|
+
sample?: Partial<import("./schema.ts").StoredValues<F> & import("./schema.ts").StoredValues<S>> | undefined;
|
|
39
|
+
}): import("./schema.ts").SectionSchema<F, S>;
|
|
40
|
+
element<F extends import("./schema.ts").BuilderMap = {}, S extends import("./schema.ts").BuilderMap = {}>(input: {
|
|
41
|
+
name: string;
|
|
42
|
+
title: string;
|
|
43
|
+
icon?: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
fields?: F | undefined;
|
|
46
|
+
settings?: S | undefined;
|
|
47
|
+
meta?: never;
|
|
48
|
+
styles?: false | {
|
|
49
|
+
omit: string[];
|
|
50
|
+
};
|
|
51
|
+
css?: false;
|
|
52
|
+
sample?: Partial<import("./schema.ts").StoredValues<F> & import("./schema.ts").StoredValues<S>> | undefined;
|
|
53
|
+
} & {
|
|
54
|
+
contexts: ("richtext" | "column")[];
|
|
55
|
+
}): import("./schema.ts").ElementSchema<F, S>;
|
|
56
|
+
};
|
|
57
|
+
collection: typeof import("./collections.ts").collection;
|
|
58
|
+
toolset: typeof import("./toolset.ts").toolset;
|
|
59
|
+
global: typeof import("./global.ts").globalItem;
|
|
60
|
+
marks: typeof import("./marks.ts").marks;
|
|
61
|
+
};
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// smoodly/react: the React half of the package (spec 2026-10-02 astro cloud fit §8; plan 2026-10-06 astro
|
|
2
|
+
// adapter 3, decision 1) — the pairing, the render layer and the view registry. A Next project's components
|
|
3
|
+
// import from here; a `.schema.ts` file and the config import from "smoodly", which loads no React, so an
|
|
4
|
+
// Astro project has none. Everything of the root is re-exported, so one import serves a component.
|
|
5
|
+
import { smoodly as core } from "./index.js";
|
|
6
|
+
import { element, section } from "./pairing.js";
|
|
7
|
+
import { page } from "./render.js";
|
|
8
|
+
import { entryPage } from "./entry-page.js";
|
|
9
|
+
import { Section } from "./section-wrapper.js";
|
|
10
|
+
export * from "./index.js";
|
|
11
|
+
export { element, entryPage, page, section, Section };
|
|
12
|
+
export { Zone, renderPage } from "./render.js";
|
|
13
|
+
export { RichText, defaultRichTextImage } from "./richtext-render.js";
|
|
14
|
+
export { defineViews } from "./views.js";
|
|
15
|
+
export { createCustomSection } from "./custom/CustomSection.js";
|
|
16
|
+
/** The root's namespace plus the React pairing: `smoodly.section(schema, View)`, `<smoodly.Section>`. */
|
|
17
|
+
export const smoodly = { ...core, section, element, page, entryPage, Section };
|
package/dist/read-shapes.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CollectionSchema } from "./collections.ts";
|
|
2
2
|
import type { EntryRecord } from "./entry-store.ts";
|
|
3
3
|
import type { LocaleAlternate, PageAlternate } from "./paths.ts";
|
|
4
|
-
import type { PageLink } from "./
|
|
4
|
+
import type { PageLink } from "./page-context.ts";
|
|
5
5
|
import type { ResolvedImage } from "./media.ts";
|
|
6
6
|
import type { SitePage } from "./site.ts";
|
|
7
7
|
import type { StyleProps } from "./styles.ts";
|
package/dist/reads.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { CollectionSchema } from "./collections.ts";
|
|
2
|
+
import type { GlobalSchema } from "./global.ts";
|
|
3
|
+
import type { PathTarget } from "./paths.ts";
|
|
4
|
+
import { type EntryItem, type PageItem } from "./read-shapes.ts";
|
|
5
|
+
import type { Materialize } from "./schema.ts";
|
|
6
|
+
import type { AddressWithGlobals, EntryQuery, PageTreeItem, SiteHit, SmoodlySite } from "./site.ts";
|
|
7
|
+
/** One content read, as the framework's cache needs it. */
|
|
8
|
+
export type ReadUnit<T> = {
|
|
9
|
+
/** What the published read holds: two calls with one key may share a cached `read(false)`. A draft read
|
|
10
|
+
* has no key of its own — the draft and a stored version read under the same one — so it is never
|
|
11
|
+
* cached by it. The site's model key rides in it. */
|
|
12
|
+
key: string[];
|
|
13
|
+
/** The tags the admin's operations expire it by (revalidate.ts). */
|
|
14
|
+
tags: string[];
|
|
15
|
+
/** Tags only the value knows (a read by address learns the id from the document it returns). An adapter
|
|
16
|
+
* that tags a response after the read adds them; Next's cache needs its tags before the read and ignores this. */
|
|
17
|
+
tagsOf?(value: T): string[];
|
|
18
|
+
/** The read: the live draft, or the published value. */
|
|
19
|
+
read(draft: boolean): Promise<T>;
|
|
20
|
+
};
|
|
21
|
+
/** What a framework supplies. Every read of content passes through `run`, so an adapter that caches whole
|
|
22
|
+
* responses hears the tags of everything a response rendered. A path lookup passes no unit: `locate`, a
|
|
23
|
+
* redirect, and a path that leads nowhere (a null from `getPage` or `getEntry`, a 404) reach no `run`. */
|
|
24
|
+
export type ReadAdapter = {
|
|
25
|
+
/** Resolves when there is a request to read for. Next: `connection()`, so a build prerenders nothing
|
|
26
|
+
* against the database. Absent where a build may read (Astro's static mode). */
|
|
27
|
+
gate?(): Promise<void>;
|
|
28
|
+
/** Runs a unit: the adapter decides draft or published, where the value is cached, and who hears its tags. */
|
|
29
|
+
run<T>(unit: ReadUnit<T>): Promise<T>;
|
|
30
|
+
};
|
|
31
|
+
export type QueryOptions = {
|
|
32
|
+
locale?: string;
|
|
33
|
+
};
|
|
34
|
+
export type EntriesQuery = Omit<EntryQuery, "locale" | "draft" | "ids"> & QueryOptions;
|
|
35
|
+
type ByName<T, N> = Extract<T, {
|
|
36
|
+
name: N;
|
|
37
|
+
}>;
|
|
38
|
+
/** Fields typed from the schema, references materialized as `EntryRef`s; a widened `CollectionSchema[]` array (names are `string`) falls back to the untyped record instead of `never`. */
|
|
39
|
+
type EntryFields<C extends readonly CollectionSchema<any, string>[], N> = string extends C[number]["name"] ? Record<string, unknown> : {
|
|
40
|
+
[K in keyof NonNullable<ByName<C[number], N>["__entry"]>]: Materialize<NonNullable<ByName<C[number], N>["__entry"]>[K]>;
|
|
41
|
+
};
|
|
42
|
+
/** A read item whose `fields` are the collection's typed values. */
|
|
43
|
+
export type TypedEntryItem<C extends readonly CollectionSchema<any, string>[], N> = EntryItem<EntryFields<C, N>>;
|
|
44
|
+
export type GlobalValue<S extends readonly GlobalSchema<any, string>[], N> = string extends S[number]["name"] ? Record<string, unknown> : NonNullable<ByName<S[number], N>["__value"]>;
|
|
45
|
+
export type SmoodlyQueries<C extends readonly CollectionSchema<any, string>[], S extends readonly GlobalSchema<any, string>[]> = {
|
|
46
|
+
/** The published page tree in a locale, every item with its page fields. */
|
|
47
|
+
getPageTree(options?: QueryOptions): Promise<PageTreeItem[]>;
|
|
48
|
+
/** A page by locale-relative path ("/company/about-us") or id. */
|
|
49
|
+
getPage(pathOrId: string, options?: QueryOptions): Promise<PageItem | null>;
|
|
50
|
+
/** Published entries of a collection: `where` (shared fields), `order`, `limit`, `parent` run in the store. */
|
|
51
|
+
getEntries<N extends C[number]["name"]>(collection: N, options?: EntriesQuery): Promise<TypedEntryItem<C, N>[]>;
|
|
52
|
+
getEntry<N extends C[number]["name"]>(collection: N, idOrPath: string, options?: QueryOptions): Promise<TypedEntryItem<C, N> | null>;
|
|
53
|
+
getGlobal<N extends S[number]["name"]>(item: N, options?: QueryOptions): Promise<GlobalValue<S, N> | null>;
|
|
54
|
+
};
|
|
55
|
+
/** The route's read of a located target (locate is never a unit: the id it returns IS the tag). Keyed by
|
|
56
|
+
* what the unit holds, not only where: an address that changes owner — a deploy handing it to a code
|
|
57
|
+
* record, which fires no tag — can never serve the previous occupant (spec 2026-09-29 pre-install fixes
|
|
58
|
+
* §5). `version` is History's stored version and counts inside draft mode only. */
|
|
59
|
+
export declare function hitUnit(site: SmoodlySite, target: PathTarget, locale: string, path: string, version?: string): ReadUnit<SiteHit | null>;
|
|
60
|
+
/** Astro's route read of an address: one lookup outside draft mode — the tags come with the document, so no
|
|
61
|
+
* locate comes first; in draft mode, locate then load as the editing tables need (spec 2026-10-07 delivery
|
|
62
|
+
* model §3). A redirect row answers as a redirect; a code-owned page answers where no record lives. */
|
|
63
|
+
export declare function addressUnit(site: SmoodlySite, locale: string, path: string, version?: string): ReadUnit<SiteHit | {
|
|
64
|
+
kind: "redirect";
|
|
65
|
+
target: PathTarget;
|
|
66
|
+
} | null>;
|
|
67
|
+
/** Astro's route read with the layout's global items riding along (schema v9): the published read is one
|
|
68
|
+
* `lookupWithGlobals`, and the shell seeds each value into the request scope under `globalUnit`'s key, so the
|
|
69
|
+
* layout's loaders cost no call. The tags are the hit's; a global's tag joins when the layout reads it. A draft
|
|
70
|
+
* read brings no globals: a preview reads live. */
|
|
71
|
+
export declare function addressWithGlobalsUnit(site: SmoodlySite, locale: string, path: string, items: readonly GlobalSchema<any>[], version?: string): ReadUnit<AddressWithGlobals>;
|
|
72
|
+
/** A global item's unit, by schema: the one `q.getGlobal` runs for the same item and locale. */
|
|
73
|
+
export declare function globalUnit<V>(site: SmoodlySite, item: GlobalSchema<V>, locale: string): ReadUnit<V | null>;
|
|
74
|
+
/** The typed queries over a site, run by the framework's adapter. */
|
|
75
|
+
export declare function createQueries<const C extends readonly CollectionSchema<any, string>[], const S extends readonly GlobalSchema<any, string>[] = []>(options: {
|
|
76
|
+
site: SmoodlySite;
|
|
77
|
+
collections: C;
|
|
78
|
+
globals?: S;
|
|
79
|
+
}, adapter: ReadAdapter): SmoodlyQueries<C, S>;
|
|
80
|
+
export {};
|
package/dist/reads.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { referencedCollections } from "./read-shapes.js";
|
|
2
|
+
import { collectionTag, entryTag, globalTag, pageTag, pagesTreeTag } from "./revalidate.js";
|
|
3
|
+
/** JSON with object keys sorted at every level and `undefined` dropped, so two spellings of one query share a unit. */
|
|
4
|
+
function stable(value) {
|
|
5
|
+
if (Array.isArray(value))
|
|
6
|
+
return value.map(stable);
|
|
7
|
+
if (value !== null && typeof value === "object") {
|
|
8
|
+
return Object.fromEntries(Object.entries(value)
|
|
9
|
+
.filter(([, v]) => v !== undefined)
|
|
10
|
+
.sort(([a], [b]) => (a < b ? -1 : 1))
|
|
11
|
+
.map(([k, v]) => [k, stable(v)]));
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
/** A stable key for an entries query: only what was given, normalized. */
|
|
16
|
+
function entriesKey(o) {
|
|
17
|
+
const { locale: _locale, ...rest } = o;
|
|
18
|
+
return JSON.stringify(stable(rest));
|
|
19
|
+
}
|
|
20
|
+
/** The route's read of a located target (locate is never a unit: the id it returns IS the tag). Keyed by
|
|
21
|
+
* what the unit holds, not only where: an address that changes owner — a deploy handing it to a code
|
|
22
|
+
* record, which fires no tag — can never serve the previous occupant (spec 2026-09-29 pre-install fixes
|
|
23
|
+
* §5). `version` is History's stored version and counts inside draft mode only. */
|
|
24
|
+
export function hitUnit(site, target, locale, path, version) {
|
|
25
|
+
return {
|
|
26
|
+
key: ["smoodly-site", site.cacheKey, locale, path, target.kind, target.id],
|
|
27
|
+
tags: [target.kind === "page" ? pageTag(target.id) : entryTag(target.id)],
|
|
28
|
+
read: (draft) => draft ? site.load(target, locale, path, { draft: true, ...(version !== undefined ? { version } : {}) }) : site.load(target, locale, path),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** Astro's route read of an address: one lookup outside draft mode — the tags come with the document, so no
|
|
32
|
+
* locate comes first; in draft mode, locate then load as the editing tables need (spec 2026-10-07 delivery
|
|
33
|
+
* model §3). A redirect row answers as a redirect; a code-owned page answers where no record lives. */
|
|
34
|
+
export function addressUnit(site, locale, path, version) {
|
|
35
|
+
return {
|
|
36
|
+
key: ["smoodly-site", site.cacheKey, locale, path],
|
|
37
|
+
tags: [],
|
|
38
|
+
tagsOf: tagsOfHit,
|
|
39
|
+
read: (draft) => (draft ? draftAddress(site, locale, path, version) : site.lookup(locale, path)),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** The tags an address's value brings: the page's or the entry's; none for a miss, a redirect or a code-owned page. */
|
|
43
|
+
function tagsOfHit(value) {
|
|
44
|
+
if (!value || value.kind === "redirect")
|
|
45
|
+
return [];
|
|
46
|
+
if (value.kind === "entry")
|
|
47
|
+
return [entryTag(value.entry.id)];
|
|
48
|
+
// A code-owned page has no row to expire it by.
|
|
49
|
+
return value.record.id.startsWith("code:") ? [] : [pageTag(value.record.id)];
|
|
50
|
+
}
|
|
51
|
+
/** The draft read of an address: locate, then load the draft, as the editing tables need. */
|
|
52
|
+
async function draftAddress(site, locale, path, version) {
|
|
53
|
+
const target = await site.locate(locale, path);
|
|
54
|
+
if (!target)
|
|
55
|
+
return site.codePage(locale, path);
|
|
56
|
+
if (target.redirect)
|
|
57
|
+
return { kind: "redirect", target: { kind: target.kind, id: target.id } };
|
|
58
|
+
return site.load(target, locale, path, { draft: true, ...(version !== undefined ? { version } : {}) });
|
|
59
|
+
}
|
|
60
|
+
/** Astro's route read with the layout's global items riding along (schema v9): the published read is one
|
|
61
|
+
* `lookupWithGlobals`, and the shell seeds each value into the request scope under `globalUnit`'s key, so the
|
|
62
|
+
* layout's loaders cost no call. The tags are the hit's; a global's tag joins when the layout reads it. A draft
|
|
63
|
+
* read brings no globals: a preview reads live. */
|
|
64
|
+
export function addressWithGlobalsUnit(site, locale, path, items, version) {
|
|
65
|
+
return {
|
|
66
|
+
key: ["smoodly-site", site.cacheKey, locale, path, "+globals"],
|
|
67
|
+
tags: [],
|
|
68
|
+
tagsOf: (value) => tagsOfHit(value.hit),
|
|
69
|
+
read: async (draft) => (draft ? { hit: await draftAddress(site, locale, path, version), globals: new Map() } : site.lookupWithGlobals(locale, path, items)),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** A global item's unit, by schema: the one `q.getGlobal` runs for the same item and locale. */
|
|
73
|
+
export function globalUnit(site, item, locale) {
|
|
74
|
+
return {
|
|
75
|
+
key: ["smoodly-q", site.cacheKey, "global", item.name, locale],
|
|
76
|
+
tags: [globalTag(item.name)],
|
|
77
|
+
read: (draft) => site.getGlobal(item, { locale, draft }),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** The typed queries over a site, run by the framework's adapter. */
|
|
81
|
+
export function createQueries(options, adapter) {
|
|
82
|
+
const { site } = options;
|
|
83
|
+
const collectionByName = new Map(options.collections.map((c) => [c.name, c]));
|
|
84
|
+
const globalByName = new Map((options.globals ?? []).map((s) => [s.name, s]));
|
|
85
|
+
const localeOf = (o) => o?.locale ?? site.locales.default;
|
|
86
|
+
const gate = async () => { await adapter.gate?.(); };
|
|
87
|
+
const unit = (key, tags, read) => ({ key: ["smoodly-q", site.cacheKey, ...key], tags, read });
|
|
88
|
+
/** The tag needs the id: a path is located first, outside any unit, as the route does. */
|
|
89
|
+
async function idOf(kind, locale, pathOrId) {
|
|
90
|
+
if (!pathOrId.startsWith("/"))
|
|
91
|
+
return pathOrId;
|
|
92
|
+
const target = await site.locate(locale, pathOrId);
|
|
93
|
+
return target?.kind === kind ? target.id : null;
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
async getPageTree(o) {
|
|
97
|
+
await gate();
|
|
98
|
+
const locale = localeOf(o);
|
|
99
|
+
return adapter.run(unit(["pageTree", locale], [pagesTreeTag()], (draft) => site.getPagesTree({ locale, draft })));
|
|
100
|
+
},
|
|
101
|
+
async getPage(pathOrId, o) {
|
|
102
|
+
await gate();
|
|
103
|
+
const locale = localeOf(o);
|
|
104
|
+
const id = await idOf("page", locale, pathOrId);
|
|
105
|
+
if (!id)
|
|
106
|
+
return null;
|
|
107
|
+
return adapter.run(unit(["page", locale, pathOrId], [pageTag(id)], (draft) => site.getPage(pathOrId, { locale, draft })));
|
|
108
|
+
},
|
|
109
|
+
async getEntries(collection, o = {}) {
|
|
110
|
+
await gate();
|
|
111
|
+
const locale = localeOf(o);
|
|
112
|
+
const { locale: _locale, ...query } = o;
|
|
113
|
+
// The unit also depends on every collection its references resolve into (spec 2026-09-29 content API contract §6).
|
|
114
|
+
const schema = collectionByName.get(collection);
|
|
115
|
+
const tags = [collection, ...(schema ? referencedCollections(schema, [...options.collections]) : [])];
|
|
116
|
+
return adapter.run(unit(["entries", collection, locale, entriesKey(o)], [...new Set(tags)].map(collectionTag), (draft) => site.getEntries(collection, { ...query, locale, draft })));
|
|
117
|
+
},
|
|
118
|
+
async getEntry(collection, idOrPath, o) {
|
|
119
|
+
await gate();
|
|
120
|
+
const locale = localeOf(o);
|
|
121
|
+
const id = await idOf("entry", locale, idOrPath);
|
|
122
|
+
if (!id)
|
|
123
|
+
return null;
|
|
124
|
+
return adapter.run(unit(["entry", collection, locale, idOrPath], [entryTag(id)], async (draft) => {
|
|
125
|
+
const hit = await site.getEntry(collection, idOrPath, { locale, draft });
|
|
126
|
+
// The site layer also answers a bare root slug, but the unit would sit under
|
|
127
|
+
// a tag no op ever fires — so a slug is refused here rather than cached stale.
|
|
128
|
+
if (hit && !idOrPath.startsWith("/") && hit.id !== idOrPath) {
|
|
129
|
+
throw new Error(`smoodly: getEntry("${collection}", "${idOrPath}") takes an id or a path — for a slug, pass the entry's path.`);
|
|
130
|
+
}
|
|
131
|
+
return hit;
|
|
132
|
+
}));
|
|
133
|
+
},
|
|
134
|
+
async getGlobal(name, o) {
|
|
135
|
+
const item = globalByName.get(name);
|
|
136
|
+
if (!item)
|
|
137
|
+
throw new Error(`smoodly: no global item named "${name}".`);
|
|
138
|
+
await gate();
|
|
139
|
+
return adapter.run(globalUnit(site, item, localeOf(o)));
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
package/dist/refs.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Descriptor } from "./fields.ts";
|
|
2
2
|
import type { CollectionSchema } from "./collections.ts";
|
|
3
|
-
import type { PageTree } from "./
|
|
3
|
+
import type { PageTree } from "./page-context.ts";
|
|
4
4
|
export type RefEdge = {
|
|
5
5
|
field: string;
|
|
6
6
|
targetCollection: string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type SchemaOr<S> = S | {
|
|
2
|
+
schema: S;
|
|
3
|
+
};
|
|
4
|
+
export declare function schemaOf<S extends object>(x: SchemaOr<S>): S;
|
|
5
|
+
/** The schema a public builder was given, refusing a paired component (plan 2026-10-02 registration split). */
|
|
6
|
+
export declare function schemaOnly<S extends {
|
|
7
|
+
name: string;
|
|
8
|
+
}>(x: S): S;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function schemaOf(x) {
|
|
2
|
+
const carried = x.schema;
|
|
3
|
+
return carried !== null && typeof carried === "object" ? carried : x;
|
|
4
|
+
}
|
|
5
|
+
/** The schema a public builder was given, refusing a paired component (plan 2026-10-02 registration split). */
|
|
6
|
+
export function schemaOnly(x) {
|
|
7
|
+
const carried = x.schema;
|
|
8
|
+
if (carried !== null && typeof carried === "object") {
|
|
9
|
+
if (carried.kind === "entryPage") {
|
|
10
|
+
const c = String(carried.collection);
|
|
11
|
+
throw new Error(`smoodly: an entry page is a view — list smoodly.entryPage(${c}, View) in defineViews({ pages }), not in registry.pages.`);
|
|
12
|
+
}
|
|
13
|
+
const name = String(carried.name);
|
|
14
|
+
throw new Error(`smoodly: pass the schema, not the component — "${name}" is registered by its schema (${name}Schema), and its component goes to defineViews.`);
|
|
15
|
+
}
|
|
16
|
+
return x;
|
|
17
|
+
}
|
package/dist/render.d.ts
CHANGED
|
@@ -1,94 +1,17 @@
|
|
|
1
|
-
import { type ComponentType, type ReactElement
|
|
2
|
-
import type { PageAlternate } from "./paths.ts";
|
|
1
|
+
import { type ComponentType, type ReactElement } from "react";
|
|
3
2
|
import type { PageSchema } from "./page.ts";
|
|
4
3
|
import type { BuilderMap, Values } from "./schema.ts";
|
|
5
4
|
import { type StyleValues } from "./styles.ts";
|
|
6
|
-
import { type CustomOptions } from "./custom/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
fields: Record<string, unknown>;
|
|
11
|
-
/** The developer's knobs (spec 2026-09-14 §3). */
|
|
12
|
-
settings?: Record<string, unknown>;
|
|
13
|
-
/** Smoodly's styles steps (spec 2026-09-14 §2); absent keys are Default. */
|
|
14
|
-
styles?: Record<string, unknown>;
|
|
15
|
-
/** The custom-CSS snippet (spec 2026-09-14 §4), scoped by the wrapper. */
|
|
16
|
-
css?: string;
|
|
17
|
-
};
|
|
18
|
-
/** `meta` is the page's own metadata (SEO title, description, og image).
|
|
19
|
-
* It rides inside the tree deliberately: trees are already JSONB in
|
|
20
|
-
* page_versions, so this needs no DDL change, and page meta becomes
|
|
21
|
-
* versioned with content — draft SEO edits stay unpublished, and publish
|
|
22
|
-
* moves content and meta together. */
|
|
23
|
-
export type PageTree = {
|
|
24
|
-
zones: Record<string, TreeNode[]>;
|
|
25
|
-
meta?: Record<string, unknown>;
|
|
26
|
-
fields?: Record<string, unknown>;
|
|
27
|
-
};
|
|
28
|
-
/** Where a rendered page sits in the tree — what a view needs for
|
|
29
|
-
* breadcrumbs and sub-navigation without a query (spec 2026-09-05 §5).
|
|
30
|
-
* `path` is the locale-relative CMS path (what the content API returns);
|
|
31
|
-
* `url` is that path through the app's `href` seam — `/fi/yritys` under
|
|
32
|
-
* next-intl, `/yritys` without it — so a page shape links the same way
|
|
33
|
-
* in both layouts. `fields` is the linked page's own page fields (spec
|
|
34
|
-
* 2026-09-10 page fields §3): the published tree's outside draft mode,
|
|
35
|
-
* raw (no asset resolution), defaults applied, `{}` for a folder. */
|
|
36
|
-
export type PageLink = {
|
|
37
|
-
title: string;
|
|
38
|
-
path: string;
|
|
39
|
-
hasPage: boolean;
|
|
40
|
-
fields: Record<string, unknown>;
|
|
41
|
-
};
|
|
42
|
-
export type PageContextLink = PageLink & {
|
|
43
|
-
url: string;
|
|
44
|
-
};
|
|
45
|
-
/** The page's address in one other locale, `url` through the app's href seam (spec 2026-09-15 §3). */
|
|
46
|
-
export type PageContextAlternate = PageAlternate & {
|
|
47
|
-
url: string;
|
|
48
|
-
};
|
|
49
|
-
export type PageContext<Fields = Record<string, unknown>> = {
|
|
50
|
-
id: string;
|
|
51
|
-
title: string;
|
|
52
|
-
path: string;
|
|
53
|
-
url: string;
|
|
54
|
-
/** The locale the page was rendered in. */
|
|
55
|
-
locale: string;
|
|
56
|
-
/** The page's own fields, typed from its schema on the view; resolved, defaults applied. */
|
|
57
|
-
fields: Fields;
|
|
58
|
-
/** Root first, parent last. */
|
|
59
|
-
ancestors: ReadonlyArray<PageContextLink>;
|
|
60
|
-
children: ReadonlyArray<PageContextLink>;
|
|
61
|
-
/** One key per supported locale: the page's `{ slug, path, url }` there, or null where it is
|
|
62
|
-
* absent or unpublished (draft mode: absent only). What a language switch links to. */
|
|
63
|
-
locales: Readonly<Record<string, PageContextAlternate | null>>;
|
|
64
|
-
};
|
|
65
|
-
/** The `page` prop of a view rendered outside the site (a test, a preview
|
|
66
|
-
* of a shape): a real object, so a view never guards an optional prop, but
|
|
67
|
-
* no position in the tree. Frozen: one singleton shared by every such
|
|
68
|
-
* render, so a view that pushed onto it would leak into the next. */
|
|
69
|
-
export declare const EMPTY_PAGE_CONTEXT: PageContext;
|
|
5
|
+
import { type CustomOptions } from "./custom/CustomSection.tsx";
|
|
6
|
+
import { type EditorWrap, type PageContext, type PageTree, type ZoneContent } from "./page-context.ts";
|
|
7
|
+
export type { TreeNode, PageTree, PageLink, PageContextLink, PageContextAlternate, PageContext, ZoneContent, EditorWrap } from "./page-context.ts";
|
|
8
|
+
export { EMPTY_PAGE_CONTEXT } from "./page-context.ts";
|
|
70
9
|
type AnyPaired = ComponentType<any> & {
|
|
71
10
|
schema: {
|
|
72
11
|
name: string;
|
|
73
12
|
title?: string;
|
|
74
13
|
};
|
|
75
14
|
};
|
|
76
|
-
/** The adapter's canvas net (spec 2026-09-27 section guard §5): called per section, in editor mode only. */
|
|
77
|
-
export type EditorWrap = (child: ReactNode, section: {
|
|
78
|
-
id: string;
|
|
79
|
-
title: string;
|
|
80
|
-
}) => ReactNode;
|
|
81
|
-
export type ZoneContent = {
|
|
82
|
-
readonly name: string;
|
|
83
|
-
readonly nodes: TreeNode[];
|
|
84
|
-
readonly components: ReadonlyMap<string, AnyPaired>;
|
|
85
|
-
/** The config's CSS values behind the styles set (spec 2026-09-14 §2). */
|
|
86
|
-
readonly styleValues: StyleValues;
|
|
87
|
-
readonly editor: boolean;
|
|
88
|
-
/** Control flow the guard must let through — the adapter's (Next: unstable_rethrow; spec 2026-09-27 section guard §2). */
|
|
89
|
-
readonly rethrow?: (error: unknown) => void;
|
|
90
|
-
readonly editorWrap?: EditorWrap;
|
|
91
|
-
};
|
|
92
15
|
export declare function Zone({ of: content }: {
|
|
93
16
|
of: ZoneContent;
|
|
94
17
|
}): import("react").JSX.Element | null;
|
|
@@ -114,4 +37,3 @@ export declare function renderPage<Zones extends Record<string, unknown>>(Page:
|
|
|
114
37
|
rethrow?: (error: unknown) => void;
|
|
115
38
|
editorWrap?: EditorWrap;
|
|
116
39
|
}): ReactElement;
|
|
117
|
-
export {};
|
package/dist/render.js
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Fragment } from "react";
|
|
3
3
|
import { EMPTY_ATTR, NODE_ATTR, ZONE_ATTR } from "./marks.js";
|
|
4
|
-
import { SMD_KEY } from "./
|
|
4
|
+
import { SMD_KEY } from "./smd-key.js";
|
|
5
5
|
import { DEFAULT_STYLE_VALUES } from "./styles.js";
|
|
6
|
-
import { CUSTOM_NODE_TYPE
|
|
6
|
+
import { CUSTOM_NODE_TYPE } from "./custom/index.js";
|
|
7
|
+
import { createCustomSection } from "./custom/CustomSection.js";
|
|
8
|
+
import { EMPTY_PAGE_CONTEXT } from "./page-context.js";
|
|
7
9
|
import { renderGuarded } from "./section-guard.js";
|
|
8
10
|
import { PLACEHOLDER_STYLE } from "./section-crash.js";
|
|
9
|
-
|
|
10
|
-
* of a shape): a real object, so a view never guards an optional prop, but
|
|
11
|
-
* no position in the tree. Frozen: one singleton shared by every such
|
|
12
|
-
* render, so a view that pushed onto it would leak into the next. */
|
|
13
|
-
export const EMPTY_PAGE_CONTEXT = Object.freeze({
|
|
14
|
-
id: "", title: "", path: "/", url: "/", locale: "", fields: Object.freeze({}), ancestors: Object.freeze([]), children: Object.freeze([]),
|
|
15
|
-
locales: Object.freeze({}),
|
|
16
|
-
});
|
|
11
|
+
export { EMPTY_PAGE_CONTEXT } from "./page-context.js";
|
|
17
12
|
export function Zone({ of: content }) {
|
|
18
13
|
// A malformed tree (bad data, a client that skipped isPageTree) must not
|
|
19
14
|
// crash the render — treat a non-array nodes as empty rather than throw.
|
package/dist/resolve.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Descriptor } from "./fields.ts";
|
|
2
2
|
import type { CollectionSchema } from "./collections.ts";
|
|
3
|
-
import type { PageTree } from "./
|
|
3
|
+
import type { PageTree } from "./page-context.ts";
|
|
4
4
|
import { type GlobalSchema } from "./global.ts";
|
|
5
5
|
import { type AssetRecord } from "./media.ts";
|
|
6
6
|
import { type ElementSchemas } from "./richtext-walk.ts";
|
package/dist/richtext/index.d.ts
CHANGED
|
@@ -6,7 +6,11 @@ export type CreateRichTextOptions = {
|
|
|
6
6
|
/** next/image `sizes` for body images; the site's text column width. */
|
|
7
7
|
sizes?: string;
|
|
8
8
|
};
|
|
9
|
-
|
|
9
|
+
/** The bound renderer; `elements` is the list it renders, which defineViews checks against the config's richtext elements. */
|
|
10
|
+
export type BoundRichText = ((props: {
|
|
10
11
|
doc: unknown;
|
|
11
|
-
}) => ReactElement
|
|
12
|
+
}) => ReactElement) & {
|
|
13
|
+
elements: RichTextElement[];
|
|
14
|
+
};
|
|
15
|
+
export declare function createRichText({ elements, sizes }?: CreateRichTextOptions): BoundRichText;
|
|
12
16
|
export type { RichTextElement, RichTextImageProps, RichTextProps } from "../richtext-render.tsx";
|
package/dist/richtext/index.js
CHANGED
|
@@ -3,7 +3,8 @@ import { RichText } from "../richtext-render.js";
|
|
|
3
3
|
import { SmoodlyImage } from "../image/SmoodlyImage.js";
|
|
4
4
|
export function createRichText({ elements = [], sizes } = {}) {
|
|
5
5
|
const image = ({ image, caption }) => (_jsxs("figure", { children: [_jsx(SmoodlyImage, { image: image, sizes: sizes }), caption !== "" && _jsx("figcaption", { children: caption })] }));
|
|
6
|
-
|
|
6
|
+
function Bound({ doc }) {
|
|
7
7
|
return _jsx(RichText, { doc: doc, elements: elements, image: image });
|
|
8
|
-
}
|
|
8
|
+
}
|
|
9
|
+
return Object.assign(Bound, { elements });
|
|
9
10
|
}
|