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
package/dist/cli/import.js
CHANGED
|
@@ -10,8 +10,9 @@ import { join, resolve } from "node:path";
|
|
|
10
10
|
import { MEDIA_CACHE_SECONDS } from "../media.js";
|
|
11
11
|
import { CONTENT_TABLES } from "../sql-space.js";
|
|
12
12
|
import { CONFLICT_COLUMNS, FORMAT, ROW_KEY, describeImport, emptyTables, foreignRows, insertPlan, intoSpace, missingRows, notEmptyMessage, rowKey, versionMismatchMessage, } from "../space-transfer.js";
|
|
13
|
-
import { MEDIA_BUCKET, connectFromEnv, isAlreadyExists, readSchemaVersion, readSpaceRows, schemaRefusal, } from "./space-io.js";
|
|
13
|
+
import { MEDIA_BUCKET, connectFromEnv, isAlreadyExists, readSchemaVersion, readSpaceRows, rebuildSpace, rebuiltMessage, schemaRefusal, } from "./space-io.js";
|
|
14
14
|
import { readCurrentSpace } from "../current-space.js";
|
|
15
|
+
import { loadConfig } from "./load-config.js";
|
|
15
16
|
export const BATCH_ROWS = 500;
|
|
16
17
|
export const notAnExport = (dir) => `Refusing: ${dir} is not a Smoodly export (no manifest.json).`;
|
|
17
18
|
export const formatMismatch = (format) => `Refusing: this export's format is ${String(format)}; this smoodly reads format ${FORMAT}. Update smoodly.`;
|
|
@@ -57,6 +58,9 @@ export async function runImport(args, out, deps = {}) {
|
|
|
57
58
|
}
|
|
58
59
|
const newRows = missingRows(target, rows);
|
|
59
60
|
let newFiles = 0;
|
|
61
|
+
// The rebuild after the writes assembles with the app's config: a folder without one is refused before them.
|
|
62
|
+
const config = deps.config ?? (await loadConfig(cwd));
|
|
63
|
+
let rebuilt = { pages: 0, entries: 0 };
|
|
60
64
|
try {
|
|
61
65
|
// Media first, so no ready asset row ever points at a missing file.
|
|
62
66
|
for (const asset of rows.assets) {
|
|
@@ -84,6 +88,9 @@ export async function runImport(args, out, deps = {}) {
|
|
|
84
88
|
throw new Error(`inserting ${table}: ${res.error.message}`);
|
|
85
89
|
}
|
|
86
90
|
}
|
|
91
|
+
// The delivery tables are derived, never exported: rebuilt from what landed (spec 2026-10-07 delivery model §2).
|
|
92
|
+
// Also on a run that landed nothing new, so a run stopped here is finished by running it again.
|
|
93
|
+
rebuilt = await rebuildSpace(db, config);
|
|
87
94
|
}
|
|
88
95
|
catch (e) {
|
|
89
96
|
out(`Import stopped: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -92,9 +99,11 @@ export async function runImport(args, out, deps = {}) {
|
|
|
92
99
|
}
|
|
93
100
|
if (newRows === 0 && newFiles === 0) {
|
|
94
101
|
out(nothingNew(space));
|
|
102
|
+
out(rebuiltMessage(rebuilt));
|
|
95
103
|
return 0;
|
|
96
104
|
}
|
|
97
105
|
out(`Imported ${describeImport(rows)} into space ${space}.`);
|
|
106
|
+
out(rebuiltMessage(rebuilt));
|
|
98
107
|
if (manifest.members.length > 0) {
|
|
99
108
|
out(MEMBERS_HEADING);
|
|
100
109
|
out(` ${manifest.members.map((m) => `${m.email ?? "(no email)"} (${m.role})`).join(", ")}`);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// packages/smoodly/src/cli/load-collections.ts
|
|
2
2
|
// Loads the app's `smoodly/collections.ts` — TypeScript, through tsx's
|
|
3
3
|
// import API, under the app's own tsconfig so its `@/` alias resolves.
|
|
4
|
-
// The barrel and not `smoodly/config.ts`: the
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// The barrel and not `smoodly/config.ts`: the collections are all a
|
|
5
|
+
// migration needs, and the barrel is what the config registers in both
|
|
6
|
+
// starters. (Since the registration split the config imports no
|
|
7
|
+
// component either; a command that needs the whole registry may load it.)
|
|
7
8
|
import { existsSync } from "node:fs";
|
|
8
9
|
import { join } from "node:path";
|
|
9
10
|
import { pathToFileURL } from "node:url";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// packages/smoodly/src/cli/load-config.ts
|
|
2
|
+
// Loads the app's whole config — TypeScript, through tsx's import API,
|
|
3
|
+
// under the app's own tsconfig so its `@/` alias resolves — for a command
|
|
4
|
+
// that assembles documents (import's rebuild, spec 2026-10-07 delivery
|
|
5
|
+
// model §2). The config imports no component (the registration split), so
|
|
6
|
+
// it loads outside the app. Astro keeps it at the root, Next under smoodly/.
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { pathToFileURL } from "node:url";
|
|
10
|
+
import { tsImport } from "tsx/esm/api";
|
|
11
|
+
export const CONFIG_FILES = ["smoodly.config.ts", "smoodly/config.ts", "src/smoodly/config.ts"];
|
|
12
|
+
export const noConfig = (cwd) => `smoodly: no ${CONFIG_FILES.join(", ")} in ${cwd} — run this in your app's folder.`;
|
|
13
|
+
export async function loadConfig(cwd) {
|
|
14
|
+
const file = CONFIG_FILES.map((name) => join(cwd, name)).find((path) => existsSync(path));
|
|
15
|
+
if (!file)
|
|
16
|
+
throw new Error(noConfig(cwd));
|
|
17
|
+
const tsconfig = join(cwd, "tsconfig.json");
|
|
18
|
+
const mod = (await tsImport(pathToFileURL(file).href, {
|
|
19
|
+
parentURL: import.meta.url,
|
|
20
|
+
...(existsSync(tsconfig) ? { tsconfig } : {}),
|
|
21
|
+
}));
|
|
22
|
+
if (!mod.config?.registry) {
|
|
23
|
+
throw new Error(`smoodly: ${file} must export \`config\`, the result of defineConfig(…).`);
|
|
24
|
+
}
|
|
25
|
+
return mod.config;
|
|
26
|
+
}
|
package/dist/cli/main.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { type MigrateArgs } from "./migrate.ts";
|
|
|
2
2
|
import { type ExportArgs } from "./export.ts";
|
|
3
3
|
import { type ImportArgs } from "./import.ts";
|
|
4
4
|
import { type AddAdminArgs } from "./add-admin.ts";
|
|
5
|
-
|
|
5
|
+
import { type RebuildArgs } from "./rebuild.ts";
|
|
6
|
+
export declare const USAGE = "usage: smoodly <command> (smoodly --version prints the version)\n\n migrate [--check] [--collections <file>] [--migrations <dir>]\n Copies the core schema files this package ships that supabase/migrations/\n lacks, and writes one views file for the collections whose view is new or\n changed. Prints the push or reset command; never runs it.\n\n --check write nothing; exit 1 when anything is pending, or when\n SMOODLY_SECRET_KEY (if set) cannot read the database\n --collections <file> the module exporting `collections` (default\n smoodly/collections.ts, or src/smoodly/collections.ts\n when only that one exists)\n --migrations <dir> the app's migration folder (default supabase/migrations)\n\n export [--out <dir>]\n Writes the space SMOODLY_SECRET_KEY names \u2014 every content row, history\n included, and the media files \u2014 into a folder (default\n smoodly-export-<date>). Reads only.\n\n import <dir>\n Writes an export folder into the key's space, which must be empty or hold\n only this export's rows (an earlier run to continue). Never deletes. Ends\n by rebuilding the delivery tables with the app's config (smoodly.config.ts,\n smoodly/config.ts or src/smoodly/config.ts), so run it in the app's folder.\n\n rebuild\n Rewrites the delivery tables of the key's space: the finished document of\n every published page, entry and global, assembled with the app's config.\n A publish keeps them current and a document behind the content model is\n rebuilt by its first visitor; run this after a deploy for a warm start.\n\n add-admin <email> [--admin-url <url>]\n Makes <email> an admin of the key's space: with SMOODLY_ADMIN_PASSWORD\n set, creates the account with that password; without, mails an invite\n whose link lands on --admin-url (default: SITE_URL + /admin).";
|
|
6
7
|
export type Parsed = {
|
|
7
8
|
command: "migrate";
|
|
8
9
|
args: MigrateArgs;
|
|
@@ -15,8 +16,13 @@ export type Parsed = {
|
|
|
15
16
|
} | {
|
|
16
17
|
command: "add-admin";
|
|
17
18
|
args: AddAdminArgs;
|
|
19
|
+
} | {
|
|
20
|
+
command: "rebuild";
|
|
21
|
+
args: RebuildArgs;
|
|
18
22
|
} | {
|
|
19
23
|
command: "help";
|
|
24
|
+
} | {
|
|
25
|
+
command: "version";
|
|
20
26
|
} | {
|
|
21
27
|
command: "error";
|
|
22
28
|
message: string;
|
package/dist/cli/main.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
// packages/smoodly/src/cli/main.ts
|
|
2
|
-
// `smoodly` — the package's command line.
|
|
2
|
+
// `smoodly` — the package's command line. Five subcommands: migrate
|
|
3
3
|
// (spec 2026-09-26 §2), export and import (spec 2026-09-28), add-admin
|
|
4
|
-
// (spec 2026-09-30 tester findings §1). Exit codes:
|
|
4
|
+
// (spec 2026-09-30 tester findings §1), rebuild (spec 2026-10-07 delivery model §4). Exit codes:
|
|
5
5
|
// 0 done or nothing to do, 1 pending (--check), refused or failed, 2 usage.
|
|
6
6
|
import { resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
-
import { runMigrate } from "./migrate.js";
|
|
8
|
+
import { DEFAULT_COLLECTIONS, runMigrate } from "./migrate.js";
|
|
9
9
|
import { runExport } from "./export.js";
|
|
10
10
|
import { runImport } from "./import.js";
|
|
11
11
|
import { runAddAdmin } from "./add-admin.js";
|
|
12
|
-
|
|
12
|
+
import { runRebuild } from "./rebuild.js";
|
|
13
|
+
import { SMOODLY_VERSION } from "../version.js";
|
|
14
|
+
export const USAGE = `usage: smoodly <command> (smoodly --version prints the version)
|
|
13
15
|
|
|
14
16
|
migrate [--check] [--collections <file>] [--migrations <dir>]
|
|
15
17
|
Copies the core schema files this package ships that supabase/migrations/
|
|
@@ -18,7 +20,9 @@ export const USAGE = `usage: smoodly <command>
|
|
|
18
20
|
|
|
19
21
|
--check write nothing; exit 1 when anything is pending, or when
|
|
20
22
|
SMOODLY_SECRET_KEY (if set) cannot read the database
|
|
21
|
-
--collections <file> the module exporting \`collections\` (default
|
|
23
|
+
--collections <file> the module exporting \`collections\` (default
|
|
24
|
+
smoodly/collections.ts, or src/smoodly/collections.ts
|
|
25
|
+
when only that one exists)
|
|
22
26
|
--migrations <dir> the app's migration folder (default supabase/migrations)
|
|
23
27
|
|
|
24
28
|
export [--out <dir>]
|
|
@@ -28,7 +32,15 @@ export const USAGE = `usage: smoodly <command>
|
|
|
28
32
|
|
|
29
33
|
import <dir>
|
|
30
34
|
Writes an export folder into the key's space, which must be empty or hold
|
|
31
|
-
only this export's rows (an earlier run to continue). Never deletes.
|
|
35
|
+
only this export's rows (an earlier run to continue). Never deletes. Ends
|
|
36
|
+
by rebuilding the delivery tables with the app's config (smoodly.config.ts,
|
|
37
|
+
smoodly/config.ts or src/smoodly/config.ts), so run it in the app's folder.
|
|
38
|
+
|
|
39
|
+
rebuild
|
|
40
|
+
Rewrites the delivery tables of the key's space: the finished document of
|
|
41
|
+
every published page, entry and global, assembled with the app's config.
|
|
42
|
+
A publish keeps them current and a document behind the content model is
|
|
43
|
+
rebuilt by its first visitor; run this after a deploy for a warm start.
|
|
32
44
|
|
|
33
45
|
add-admin <email> [--admin-url <url>]
|
|
34
46
|
Makes <email> an admin of the key's space: with SMOODLY_ADMIN_PASSWORD
|
|
@@ -38,6 +50,8 @@ export function parseArgs(argv) {
|
|
|
38
50
|
const [command, ...rest] = argv;
|
|
39
51
|
if (!command || command === "help" || argv.some((a) => a === "--help" || a === "-h"))
|
|
40
52
|
return { command: "help" };
|
|
53
|
+
if (command === "--version" || command === "-v")
|
|
54
|
+
return { command: "version" };
|
|
41
55
|
if (command === "export") {
|
|
42
56
|
const args = { out: null };
|
|
43
57
|
for (let i = 0; i < rest.length; i++) {
|
|
@@ -54,6 +68,11 @@ export function parseArgs(argv) {
|
|
|
54
68
|
return { command: "error", message: "import needs one export folder: smoodly import <dir>" };
|
|
55
69
|
return { command: "import", args: { dir: rest[0] } };
|
|
56
70
|
}
|
|
71
|
+
if (command === "rebuild") {
|
|
72
|
+
if (rest.length > 0)
|
|
73
|
+
return { command: "error", message: `unexpected argument "${rest[0]}"` };
|
|
74
|
+
return { command: "rebuild", args: {} };
|
|
75
|
+
}
|
|
57
76
|
if (command === "add-admin") {
|
|
58
77
|
const usage = { command: "error", message: "add-admin needs one email: smoodly add-admin <email> [--admin-url <url>]" };
|
|
59
78
|
const [email, ...flags] = rest;
|
|
@@ -67,7 +86,7 @@ export function parseArgs(argv) {
|
|
|
67
86
|
}
|
|
68
87
|
if (command !== "migrate")
|
|
69
88
|
return { command: "error", message: `unknown command "${command}"` };
|
|
70
|
-
const args = { check: false, collections:
|
|
89
|
+
const args = { check: false, collections: DEFAULT_COLLECTIONS, migrations: "supabase/migrations" };
|
|
71
90
|
for (let i = 0; i < rest.length; i++) {
|
|
72
91
|
const a = rest[i];
|
|
73
92
|
if (a === "--check")
|
|
@@ -87,6 +106,10 @@ export async function main(argv, out = console.log) {
|
|
|
87
106
|
out(USAGE);
|
|
88
107
|
return 0;
|
|
89
108
|
}
|
|
109
|
+
if (parsed.command === "version") {
|
|
110
|
+
out(SMOODLY_VERSION);
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
90
113
|
if (parsed.command === "error") {
|
|
91
114
|
out(`smoodly: ${parsed.message}\n\n${USAGE}`);
|
|
92
115
|
return 2;
|
|
@@ -98,6 +121,8 @@ export async function main(argv, out = console.log) {
|
|
|
98
121
|
return await runExport(parsed.args, out);
|
|
99
122
|
if (parsed.command === "add-admin")
|
|
100
123
|
return await runAddAdmin(parsed.args, out);
|
|
124
|
+
if (parsed.command === "rebuild")
|
|
125
|
+
return await runRebuild(parsed.args, out);
|
|
101
126
|
return await runMigrate(parsed.args, out);
|
|
102
127
|
}
|
|
103
128
|
catch (e) {
|
package/dist/cli/migrate.d.ts
CHANGED
|
@@ -6,6 +6,9 @@ export type MigrateArgs = {
|
|
|
6
6
|
collections: string;
|
|
7
7
|
migrations: string;
|
|
8
8
|
};
|
|
9
|
+
export declare const DEFAULT_COLLECTIONS = "smoodly/collections.ts";
|
|
10
|
+
/** The Astro starter keeps the setup under src/ (plan 2026-10-06 astro adapter 3); the default follows it when the Next path is absent. */
|
|
11
|
+
export declare function collectionsFile(cwd: string, given: string, exists?: (p: string) => boolean): string;
|
|
9
12
|
/** The folder shipped in the package: `../../migrations/` from dist/cli/ and from src/cli/ alike. */
|
|
10
13
|
export declare const PACKAGE_MIGRATIONS_DIR: string;
|
|
11
14
|
export declare const NO_FOLDER: (dir: string) => string;
|
package/dist/cli/migrate.js
CHANGED
|
@@ -15,6 +15,13 @@ import { resolveSmoodlyEnv } from "../env.js";
|
|
|
15
15
|
import { loadAppEnv } from "./space-io.js";
|
|
16
16
|
import { planIsEmpty, planMigrate } from "../migrations.js";
|
|
17
17
|
import { loadCollections } from "./load-collections.js";
|
|
18
|
+
export const DEFAULT_COLLECTIONS = "smoodly/collections.ts";
|
|
19
|
+
/** The Astro starter keeps the setup under src/ (plan 2026-10-06 astro adapter 3); the default follows it when the Next path is absent. */
|
|
20
|
+
export function collectionsFile(cwd, given, exists = existsSync) {
|
|
21
|
+
if (given !== DEFAULT_COLLECTIONS || exists(resolve(cwd, given)))
|
|
22
|
+
return given;
|
|
23
|
+
return exists(resolve(cwd, "src/smoodly/collections.ts")) ? "src/smoodly/collections.ts" : given;
|
|
24
|
+
}
|
|
18
25
|
/** The folder shipped in the package: `../../migrations/` from dist/cli/ and from src/cli/ alike. */
|
|
19
26
|
export const PACKAGE_MIGRATIONS_DIR = fileURLToPath(new URL("../../migrations/", import.meta.url));
|
|
20
27
|
export const NO_FOLDER = (dir) => `No supabase/migrations folder here (${dir}) — nothing to do. Run it in your app's folder, or pass --migrations. (Smoodly Cloud keeps the schema current; a self-host app gets the folder from \`supabase init\`.)`;
|
|
@@ -87,7 +94,7 @@ export async function runMigrate(args, out, deps = {}) {
|
|
|
87
94
|
out(NO_FOLDER(appDir));
|
|
88
95
|
return 0;
|
|
89
96
|
}
|
|
90
|
-
const collections = await (deps.load ?? loadCollections)(resolve(cwd, args.collections), cwd);
|
|
97
|
+
const collections = await (deps.load ?? loadCollections)(resolve(cwd, collectionsFile(cwd, args.collections)), cwd);
|
|
91
98
|
const plan = planMigrate({
|
|
92
99
|
packageFiles: await readSqlFiles(PACKAGE_MIGRATIONS_DIR),
|
|
93
100
|
appFiles: await readSqlFiles(appDir),
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import type { ResolvedConfig } from "../config.ts";
|
|
3
|
+
import { type SpaceConnection } from "./space-io.ts";
|
|
4
|
+
export type RebuildArgs = Record<string, never>;
|
|
5
|
+
export type RebuildDeps = {
|
|
6
|
+
cwd?: string;
|
|
7
|
+
connection?: SpaceConnection;
|
|
8
|
+
/** The app's config; default: loaded from the folder (load-config.ts). */
|
|
9
|
+
config?: ResolvedConfig;
|
|
10
|
+
/** Test seam: the projection; default rebuildSpace. */
|
|
11
|
+
rebuild?: (db: SupabaseClient, config: ResolvedConfig) => Promise<{
|
|
12
|
+
pages: number;
|
|
13
|
+
entries: number;
|
|
14
|
+
model: string;
|
|
15
|
+
}>;
|
|
16
|
+
};
|
|
17
|
+
/** The line a rebuild ends with: import's counts, and the model the rows now carry. */
|
|
18
|
+
export declare const rebuildMessage: (r: {
|
|
19
|
+
pages: number;
|
|
20
|
+
entries: number;
|
|
21
|
+
model: string;
|
|
22
|
+
}) => string;
|
|
23
|
+
export declare function runRebuild(_args: RebuildArgs, out: (line: string) => void, deps?: RebuildDeps): Promise<number>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { schemaBehind, schemaBehindMessage } from "../schema-version.js";
|
|
2
|
+
import { loadConfig } from "./load-config.js";
|
|
3
|
+
import { connectFromEnv, readSchemaVersion, rebuildSpace, rebuiltMessage, schemaRefusal } from "./space-io.js";
|
|
4
|
+
/** The line a rebuild ends with: import's counts, and the model the rows now carry. */
|
|
5
|
+
export const rebuildMessage = (r) => `${rebuiltMessage(r).replace(/\.$/, "")} (model ${r.model}).`;
|
|
6
|
+
export async function runRebuild(_args, out, deps = {}) {
|
|
7
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
8
|
+
const { db } = deps.connection ?? connectFromEnv(cwd);
|
|
9
|
+
const schema = await readSchemaVersion(db);
|
|
10
|
+
if ("missing" in schema) {
|
|
11
|
+
out(schemaRefusal(schema.missing, "Rebuild"));
|
|
12
|
+
return 1;
|
|
13
|
+
}
|
|
14
|
+
// A behind schema has no delivery tables (version 8) or reads columns it lacks: refused before any write.
|
|
15
|
+
if (schemaBehind(schema.version)) {
|
|
16
|
+
out(schemaBehindMessage(schema.version));
|
|
17
|
+
return 1;
|
|
18
|
+
}
|
|
19
|
+
const config = deps.config ?? (await loadConfig(cwd));
|
|
20
|
+
out(rebuildMessage(await (deps.rebuild ?? rebuildSpace)(db, config)));
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
package/dist/cli/space-io.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import type { ResolvedConfig } from "../config.ts";
|
|
2
3
|
import type { ContentTable } from "../sql-space.ts";
|
|
3
4
|
import { type Row } from "../space-transfer.ts";
|
|
4
5
|
export type SpaceConnection = {
|
|
@@ -13,7 +14,20 @@ export declare function loadAppEnv(cwd: string, env?: Record<string, string | un
|
|
|
13
14
|
* folder, so the error names the folder it looked in. */
|
|
14
15
|
export declare function connectFromEnv(cwd: string, env?: Record<string, string | undefined>): SpaceConnection;
|
|
15
16
|
export { readSchemaVersion } from "../schema-read.ts";
|
|
16
|
-
|
|
17
|
+
/** The key's space projected whole into the delivery tables (spec 2026-10-07 delivery model §2): the stores and
|
|
18
|
+
* the site built from the config as the app's glue builds them, then every page and entry. */
|
|
19
|
+
export declare function rebuildSpace(db: SupabaseClient, config: ResolvedConfig): Promise<{
|
|
20
|
+
model: string;
|
|
21
|
+
pages: number;
|
|
22
|
+
entries: number;
|
|
23
|
+
stale: number;
|
|
24
|
+
}>;
|
|
25
|
+
/** The line a rebuild ends with. */
|
|
26
|
+
export declare const rebuiltMessage: (counts: {
|
|
27
|
+
pages: number;
|
|
28
|
+
entries: number;
|
|
29
|
+
}) => string;
|
|
30
|
+
export declare function schemaRefusal(missing: "key" | "schema", command: "Export" | "Import" | "Rebuild"): string;
|
|
17
31
|
/** Every row of one table in one space, paged in primary-key order. The space filter is explicit: a service-role key sees every space. */
|
|
18
32
|
export declare function readSpaceRows(db: SupabaseClient, table: ContentTable, space: string, where?: Record<string, string>, columns?: string): Promise<Row[]>;
|
|
19
33
|
type StorageErrorLike = {
|
package/dist/cli/space-io.js
CHANGED
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { parseEnv } from "node:util";
|
|
10
|
+
import { createSupabaseStores, siteOptions } from "../bind.js";
|
|
10
11
|
import { createSmoodlyClient } from "../client.js";
|
|
12
|
+
import { projectAll } from "../project.js";
|
|
13
|
+
import { createSmoodlySite } from "../site.js";
|
|
11
14
|
import { resolveSmoodlyEnv } from "../env.js";
|
|
12
15
|
import { ROW_KEY } from "../space-transfer.js";
|
|
13
16
|
export const MEDIA_BUCKET = "media";
|
|
@@ -38,6 +41,17 @@ export function connectFromEnv(cwd, env = process.env) {
|
|
|
38
41
|
return { db: createSmoodlyClient(resolveSmoodlyEnv(env)) };
|
|
39
42
|
}
|
|
40
43
|
export { readSchemaVersion } from "../schema-read.js";
|
|
44
|
+
/** The key's space projected whole into the delivery tables (spec 2026-10-07 delivery model §2): the stores and
|
|
45
|
+
* the site built from the config as the app's glue builds them, then every page and entry. */
|
|
46
|
+
export async function rebuildSpace(db, config) {
|
|
47
|
+
const { pages, entries, assets, published } = createSupabaseStores(db, config);
|
|
48
|
+
const { projector, cacheKey } = createSmoodlySite(siteOptions(config, () => ({ pages, entries, assets, published })));
|
|
49
|
+
const names = [...(config.registry.collections ?? []), ...(config.registry.globals ?? [])].map((c) => c.name);
|
|
50
|
+
// The model fingerprint every row now carries: what a rebuild after a deploy wrote.
|
|
51
|
+
return { ...(await projectAll({ stores: { pages, entries }, projector, names })), model: cacheKey };
|
|
52
|
+
}
|
|
53
|
+
/** The line a rebuild ends with. */
|
|
54
|
+
export const rebuiltMessage = (counts) => `Rebuilt the delivery tables: ${counts.pages} ${counts.pages === 1 ? "page" : "pages"}, ${counts.entries} ${counts.entries === 1 ? "entry" : "entries"}.`;
|
|
41
55
|
export function schemaRefusal(missing, command) {
|
|
42
56
|
return missing === "key"
|
|
43
57
|
? `${command} needs SMOODLY_SECRET_KEY — the service-role key, or the cloud's write key. The publishable key reads no content.`
|
package/dist/client.js
CHANGED
|
@@ -11,10 +11,15 @@ export function clientOptions(env) {
|
|
|
11
11
|
// without one (spec 2026-09-30 tester findings §4): a self-host service role is a valid apikey.
|
|
12
12
|
return { url: env.url, key: env.publishableKey || env.secretKey, headers: { Authorization: `Bearer ${env.secretKey}` } };
|
|
13
13
|
}
|
|
14
|
+
/** Where a framework adapter hears each database call (spec 2026-10-07 delivery model): the Astro request
|
|
15
|
+
* scope installs it and counts, so a test can hold a live page to one lookup. A global, not an import:
|
|
16
|
+
* the client stays free of any adapter. */
|
|
17
|
+
const READ_COUNTER = Symbol.for("smoodly:read-counter");
|
|
14
18
|
/** fetch that names the variable behind two failures supabase-js reports bare: the network
|
|
15
19
|
* ("TypeError: fetch failed") and PostgREST refusing the key. */
|
|
16
20
|
function namedFetch(url) {
|
|
17
21
|
return async (input, init) => {
|
|
22
|
+
globalThis[READ_COUNTER]?.();
|
|
18
23
|
let res;
|
|
19
24
|
try {
|
|
20
25
|
res = await fetch(input, init);
|
package/dist/config.d.ts
CHANGED
|
@@ -5,28 +5,28 @@ import { type Addresses } from "./addresses.ts";
|
|
|
5
5
|
import type { Anchors } from "./anchors.ts";
|
|
6
6
|
import { type StyleValues, type StyleValuesInput } from "./styles.ts";
|
|
7
7
|
import { type CustomConfigInput, type ResolvedCustom } from "./custom/index.ts";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
css?: false;
|
|
8
|
+
/** A section, element or page schema as the config reads it. */
|
|
9
|
+
export type RegisteredSchema = {
|
|
10
|
+
kind: string;
|
|
11
|
+
name: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
slug?: string | Record<string, string>;
|
|
14
|
+
collection?: string;
|
|
15
|
+
fields?: Record<string, Descriptor>;
|
|
16
|
+
settings?: Record<string, Descriptor>;
|
|
17
|
+
/** The section's toggles on Smoodly's styles set and the CSS box (spec 2026-09-14 §2, §4). */
|
|
18
|
+
styles?: false | {
|
|
19
|
+
omit: string[];
|
|
21
20
|
};
|
|
21
|
+
css?: false;
|
|
22
22
|
};
|
|
23
23
|
export type SmoodlyConfig = {
|
|
24
24
|
registry: {
|
|
25
|
-
sections?:
|
|
26
|
-
elements?:
|
|
25
|
+
sections?: RegisteredSchema[];
|
|
26
|
+
elements?: RegisteredSchema[];
|
|
27
27
|
collections?: CollectionSchema<any>[];
|
|
28
|
-
/** Page
|
|
29
|
-
pages?:
|
|
28
|
+
/** Page types. Entry pages are views: they go to defineViews. */
|
|
29
|
+
pages?: RegisteredSchema[];
|
|
30
30
|
/** Global content: code-declared singletons (spec 2026-09-07). */
|
|
31
31
|
globals?: GlobalSchema[];
|
|
32
32
|
};
|
|
@@ -49,7 +49,7 @@ export type SmoodlyConfig = {
|
|
|
49
49
|
styles?: StyleValuesInput;
|
|
50
50
|
/** The custom-CSS box (spec 2026-09-14 §4): off unless true. */
|
|
51
51
|
css?: boolean;
|
|
52
|
-
/** The custom section (spec 2026-09-14 §6): the columns' toolset
|
|
52
|
+
/** The custom section (spec 2026-09-14 §6): the columns' toolset and the stacking breakpoint. The bound RichText is a view: defineViews takes it. */
|
|
53
53
|
custom?: CustomConfigInput;
|
|
54
54
|
/** Slug of the page record the site root serves. The admin maps this
|
|
55
55
|
* slug to "/" (canvas URL, Pages list) and every other slug to
|
|
@@ -69,7 +69,16 @@ export type SmoodlyConfig = {
|
|
|
69
69
|
path?: string;
|
|
70
70
|
};
|
|
71
71
|
};
|
|
72
|
-
|
|
72
|
+
/** The registry as defineConfig resolves it: schemas, and page types only under `pages`. */
|
|
73
|
+
export type Registry = {
|
|
74
|
+
sections: RegisteredSchema[];
|
|
75
|
+
elements: RegisteredSchema[];
|
|
76
|
+
collections: CollectionSchema<any>[];
|
|
77
|
+
globals: GlobalSchema[];
|
|
78
|
+
pages: RegisteredSchema[];
|
|
79
|
+
};
|
|
80
|
+
export type ResolvedConfig = Omit<SmoodlyConfig, "styles" | "custom" | "admin" | "registry"> & {
|
|
81
|
+
registry: Registry;
|
|
73
82
|
homePageSlug: string;
|
|
74
83
|
/** How deep editors may nest pages in the tree. Default 1 (flat). */
|
|
75
84
|
pageDepth: number;
|
|
@@ -98,4 +107,3 @@ export type ResolvedConfig = Omit<SmoodlyConfig, "styles" | "custom" | "admin">
|
|
|
98
107
|
* target of every media edge; `media`: the admin's Media screen route. */
|
|
99
108
|
export declare const RESERVED_NAMES: readonly string[];
|
|
100
109
|
export declare function defineConfig(config: SmoodlyConfig): ResolvedConfig;
|
|
101
|
-
export {};
|