smoodly 0.0.39 → 0.0.40
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/dist/cli/space-io.d.ts +5 -4
- package/dist/cli/space-io.js +12 -5
- package/dist/client.js +15 -1
- package/dist/content/content-api.d.ts +6 -0
- package/dist/content/content-api.js +6 -0
- package/dist/env.d.ts +5 -0
- package/dist/env.js +22 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/next/page-renderer.js +17 -7
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/cli/space-io.d.ts
CHANGED
|
@@ -7,10 +7,11 @@ export type SpaceConnection = {
|
|
|
7
7
|
export declare const MEDIA_BUCKET = "media";
|
|
8
8
|
/** Rows per request. A host may cap below it, so a read stops at the first empty page, not the first short one. */
|
|
9
9
|
export declare const PAGE_ROWS = 1000;
|
|
10
|
-
/** `.env.local`, then `.env`, as Next reads them; a variable already set wins (spec §5). */
|
|
11
|
-
export declare function loadAppEnv(cwd: string, env?: Record<string, string | undefined>):
|
|
12
|
-
/** The app's content client, from its env.
|
|
13
|
-
|
|
10
|
+
/** `.env.local`, then `.env`, as Next reads them; a variable already set wins (spec §5). Returns the files it read. */
|
|
11
|
+
export declare function loadAppEnv(cwd: string, env?: Record<string, string | undefined>): string[];
|
|
12
|
+
/** The app's content client, from its env. Run outside the app, the missing keys are a missing
|
|
13
|
+
* folder, so the error names the folder it looked in. */
|
|
14
|
+
export declare function connectFromEnv(cwd: string, env?: Record<string, string | undefined>): SpaceConnection;
|
|
14
15
|
export { readSchemaVersion } from "../schema-read.ts";
|
|
15
16
|
export declare function schemaRefusal(missing: "key" | "schema", command: "Export" | "Import"): string;
|
|
16
17
|
/** 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. */
|
package/dist/cli/space-io.js
CHANGED
|
@@ -13,22 +13,29 @@ import { ROW_KEY } from "../space-transfer.js";
|
|
|
13
13
|
export const MEDIA_BUCKET = "media";
|
|
14
14
|
/** Rows per request. A host may cap below it, so a read stops at the first empty page, not the first short one. */
|
|
15
15
|
export const PAGE_ROWS = 1000;
|
|
16
|
-
/** `.env.local`, then `.env`, as Next reads them; a variable already set wins (spec §5). */
|
|
16
|
+
/** `.env.local`, then `.env`, as Next reads them; a variable already set wins (spec §5). Returns the files it read. */
|
|
17
17
|
export function loadAppEnv(cwd, env = process.env) {
|
|
18
|
+
const read = [];
|
|
18
19
|
for (const name of [".env.local", ".env"]) {
|
|
19
20
|
const file = join(cwd, name);
|
|
20
21
|
if (!existsSync(file))
|
|
21
22
|
continue;
|
|
23
|
+
read.push(name);
|
|
22
24
|
for (const [key, value] of Object.entries(parseEnv(readFileSync(file, "utf8")))) {
|
|
23
25
|
if (env[key] === undefined)
|
|
24
26
|
env[key] = value;
|
|
25
27
|
}
|
|
26
28
|
}
|
|
29
|
+
return read;
|
|
27
30
|
}
|
|
28
|
-
/** The app's content client, from its env.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
/** The app's content client, from its env. Run outside the app, the missing keys are a missing
|
|
32
|
+
* folder, so the error names the folder it looked in. */
|
|
33
|
+
export function connectFromEnv(cwd, env = process.env) {
|
|
34
|
+
const read = loadAppEnv(cwd, env);
|
|
35
|
+
if (read.length === 0 && (!env.SMOODLY_URL || !env.SMOODLY_SECRET_KEY)) {
|
|
36
|
+
throw new Error(`smoodly: no .env.local or .env in ${cwd} — run this in your app's folder.`);
|
|
37
|
+
}
|
|
38
|
+
return { db: createSmoodlyClient(resolveSmoodlyEnv(env)) };
|
|
32
39
|
}
|
|
33
40
|
export { readSchemaVersion } from "../schema-read.js";
|
|
34
41
|
export function schemaRefusal(missing, command) {
|
package/dist/client.js
CHANGED
|
@@ -11,11 +11,25 @@ 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
|
+
/** fetch that names the address when the network fails: supabase-js reports only
|
|
15
|
+
* "TypeError: fetch failed", which names neither the variable nor the URL. */
|
|
16
|
+
function namedFetch(url) {
|
|
17
|
+
return async (input, init) => {
|
|
18
|
+
try {
|
|
19
|
+
return await fetch(input, init);
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
if (e instanceof Error && e.name === "AbortError")
|
|
23
|
+
throw e;
|
|
24
|
+
throw new TypeError(`can't reach SMOODLY_URL (${url}): ${e instanceof Error ? e.message : String(e)}`);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
14
28
|
/** The server-side client the stores are built on. */
|
|
15
29
|
export function createSmoodlyClient(env) {
|
|
16
30
|
const options = clientOptions(env);
|
|
17
31
|
return createClient(options.url, options.key, {
|
|
18
|
-
global: { headers: options.headers },
|
|
32
|
+
global: { headers: options.headers, fetch: namedFetch(options.url) },
|
|
19
33
|
auth: { persistSession: false, autoRefreshToken: false },
|
|
20
34
|
});
|
|
21
35
|
}
|
|
@@ -43,6 +43,12 @@ export declare function createSmoodlyContent(deps: ContentDeps): {
|
|
|
43
43
|
title?: string;
|
|
44
44
|
}): Promise<PageRecord>;
|
|
45
45
|
publish: (id: string, locale: string) => Promise<PageRecord>;
|
|
46
|
+
/** Every page present in `locale` (the default language unless given), code-owned ones included and
|
|
47
|
+
* folders left out, optionally only one page type: what a script walks to change many pages. */
|
|
48
|
+
list(options?: {
|
|
49
|
+
locale?: string;
|
|
50
|
+
template?: string;
|
|
51
|
+
}): Promise<PageRecord[]>;
|
|
46
52
|
/** A code-owned address with no record gets it created the way the admin's Pages list does (spec §2). */
|
|
47
53
|
find(locale: string, path: string): Promise<PageRecord | null>;
|
|
48
54
|
};
|
|
@@ -107,6 +107,12 @@ export function createSmoodlyContent(deps) {
|
|
|
107
107
|
}));
|
|
108
108
|
},
|
|
109
109
|
publish: (id, locale) => must(ops.pages.publish(id, locale)),
|
|
110
|
+
/** Every page present in `locale` (the default language unless given), code-owned ones included and
|
|
111
|
+
* folders left out, optionally only one page type: what a script walks to change many pages. */
|
|
112
|
+
async list(options = {}) {
|
|
113
|
+
const locale = options.locale ?? def;
|
|
114
|
+
return (await must(ops.pages.list())).filter((p) => p.template !== null && p.locales[locale] !== undefined && (options.template === undefined || p.template === options.template));
|
|
115
|
+
},
|
|
110
116
|
/** A code-owned address with no record gets it created the way the admin's Pages list does (spec §2). */
|
|
111
117
|
async find(locale, path) {
|
|
112
118
|
const found = await findPage(locale, path);
|
package/dist/env.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export type SmoodlyEnv = {
|
|
|
15
15
|
* publishable key, so their callers pass "admin" (spec 2026-09-30 tester findings §4). A key
|
|
16
16
|
* in the wrong variable or of no known form is named here, not by a failed read later. */
|
|
17
17
|
export declare function resolveSmoodlyEnv(env: Record<string, string | undefined>, need?: "site" | "admin"): SmoodlyEnv;
|
|
18
|
+
/** The site's own address for the root layout's `metadataBase`, so canonical, hreflang and social
|
|
19
|
+
* image links are absolute. A value without a scheme is named here, not by a bare "Invalid URL" on
|
|
20
|
+
* every page; an unset one falls back to localhost and warns once in production, where it would
|
|
21
|
+
* put localhost into every page's head. */
|
|
22
|
+
export declare function siteUrl(env?: Record<string, string | undefined>): URL;
|
|
18
23
|
/** `resolveSmoodlyEnv(process.env)` — call lazily, never at module load,
|
|
19
24
|
* so `next build` without env still succeeds. */
|
|
20
25
|
export declare const smoodlyEnv: (need?: "site" | "admin") => SmoodlyEnv;
|
package/dist/env.js
CHANGED
|
@@ -41,8 +41,30 @@ export function resolveSmoodlyEnv(env, need = "site") {
|
|
|
41
41
|
throw new Error("smoodly: SMOODLY_PUBLISHABLE_KEY is neither an sb_publishable_ key nor a JWT — copy the publishable (anon) key from your Supabase project.");
|
|
42
42
|
}
|
|
43
43
|
const url = env.SMOODLY_URL;
|
|
44
|
+
if (!/^https?:\/\/[^/\s]+/.test(url)) {
|
|
45
|
+
throw new Error(`smoodly: SMOODLY_URL "${url}" is not an http(s) address — copy the project URL from your Supabase project.`);
|
|
46
|
+
}
|
|
44
47
|
return { url, secretKey, publishableKey, authUrl: env.SMOODLY_AUTH_URL || url, authKey: env.SMOODLY_AUTH_KEY || publishableKey };
|
|
45
48
|
}
|
|
49
|
+
let siteUrlWarned = false;
|
|
50
|
+
/** The site's own address for the root layout's `metadataBase`, so canonical, hreflang and social
|
|
51
|
+
* image links are absolute. A value without a scheme is named here, not by a bare "Invalid URL" on
|
|
52
|
+
* every page; an unset one falls back to localhost and warns once in production, where it would
|
|
53
|
+
* put localhost into every page's head. */
|
|
54
|
+
export function siteUrl(env = process.env) {
|
|
55
|
+
const value = env.SITE_URL;
|
|
56
|
+
if (!value) {
|
|
57
|
+
if (env.NODE_ENV === "production" && !siteUrlWarned) {
|
|
58
|
+
siteUrlWarned = true;
|
|
59
|
+
console.warn(`smoodly: SITE_URL is not set — the page head's links point at http://localhost:3000 (Deploying: ${DEPLOYING}).`);
|
|
60
|
+
}
|
|
61
|
+
return new URL("http://localhost:3000");
|
|
62
|
+
}
|
|
63
|
+
if (!/^https?:\/\/[^/\s]+/.test(value)) {
|
|
64
|
+
throw new Error(`smoodly: SITE_URL "${value}" is not an http(s) address — set it like https://www.example.com.`);
|
|
65
|
+
}
|
|
66
|
+
return new URL(value);
|
|
67
|
+
}
|
|
46
68
|
/** `resolveSmoodlyEnv(process.env)` — call lazily, never at module load,
|
|
47
69
|
* so `next build` without env still succeeds. */
|
|
48
70
|
export const smoodlyEnv = (need = "site") => resolveSmoodlyEnv(process.env, need);
|
package/dist/index.d.ts
CHANGED
|
@@ -129,7 +129,7 @@ export declare const smoodly: {
|
|
|
129
129
|
};
|
|
130
130
|
export { NODE_ATTR, ZONE_ATTR, EMPTY_ATTR, BLANK_ATTR, FIELD_ATTR, ITEM_ATTR, EDITOR_ATTR, resolveMarkPath } from "./marks.ts";
|
|
131
131
|
export type { MarkNode, MarkPath, Marks, Scope } from "./marks.ts";
|
|
132
|
-
export { resolveSmoodlyEnv, smoodlyEnv, presenceConfig } from "./env.ts";
|
|
132
|
+
export { resolveSmoodlyEnv, smoodlyEnv, presenceConfig, siteUrl } from "./env.ts";
|
|
133
133
|
export type { SmoodlyEnv, PresenceConfig } from "./env.ts";
|
|
134
134
|
export { clientOptions, createSmoodlyClient } from "./client.ts";
|
|
135
135
|
export { createSmoodlySite, pageContextOf } from "./site.ts";
|
package/dist/index.js
CHANGED
|
@@ -67,6 +67,6 @@ export const smoodly = {
|
|
|
67
67
|
marks,
|
|
68
68
|
};
|
|
69
69
|
export { NODE_ATTR, ZONE_ATTR, EMPTY_ATTR, BLANK_ATTR, FIELD_ATTR, ITEM_ATTR, EDITOR_ATTR, resolveMarkPath } from "./marks.js";
|
|
70
|
-
export { resolveSmoodlyEnv, smoodlyEnv, presenceConfig } from "./env.js";
|
|
70
|
+
export { resolveSmoodlyEnv, smoodlyEnv, presenceConfig, siteUrl } from "./env.js";
|
|
71
71
|
export { clientOptions, createSmoodlyClient } from "./client.js";
|
|
72
72
|
export { createSmoodlySite, pageContextOf } from "./site.js";
|
|
@@ -76,15 +76,19 @@ export function createPageRenderer(options) {
|
|
|
76
76
|
}
|
|
77
77
|
return _jsx(View, { entry: toEntryItem(hit.entry, hit.entry.fields, hit) });
|
|
78
78
|
}
|
|
79
|
-
/**
|
|
80
|
-
|
|
79
|
+
/** The page's own address as its canonical, and hreflang from the SAME map a language switch reads
|
|
80
|
+
* (spec 2026-09-15 §3): the locales with a URL, through href. Next makes both absolute from the
|
|
81
|
+
* layout's `metadataBase` (tester run on 0.0.39, mission 6). */
|
|
82
|
+
function addresses(hit, locale) {
|
|
83
|
+
const own = hit.locales[locale]?.path;
|
|
84
|
+
const out = own ? { canonical: href(own, locale) } : {};
|
|
81
85
|
if (site.locales.supported.length < 2)
|
|
82
|
-
return
|
|
86
|
+
return out;
|
|
83
87
|
const languages = {};
|
|
84
|
-
for (const [
|
|
88
|
+
for (const [l, alt] of Object.entries(hit.locales))
|
|
85
89
|
if (alt?.path)
|
|
86
|
-
languages[
|
|
87
|
-
return {
|
|
90
|
+
languages[l] = href(alt.path, l);
|
|
91
|
+
return { ...out, languages };
|
|
88
92
|
}
|
|
89
93
|
// Next's cacheComponents serves the catch-all from a fallback shell that
|
|
90
94
|
// commits 200 before the page renders, so 404s and 308s are lost (spec
|
|
@@ -137,7 +141,13 @@ export function createPageRenderer(options) {
|
|
|
137
141
|
return {};
|
|
138
142
|
// A page type without seo: true still gets its page title as <title>; seo: true adds the panel's values on top.
|
|
139
143
|
const seo = hit.seo ?? (hit.kind === "page" ? { title: hit.title } : undefined);
|
|
140
|
-
|
|
144
|
+
const meta = metadataFromSeo(seo);
|
|
145
|
+
const { canonical, languages } = addresses(hit, locale);
|
|
146
|
+
// og:url only beside Open Graph values the page already sets: an openGraph of its own would
|
|
147
|
+
// replace the layout's defaults (Next merges metadata one key deep).
|
|
148
|
+
if (canonical && meta.openGraph)
|
|
149
|
+
meta.openGraph = { ...meta.openGraph, url: canonical };
|
|
150
|
+
return canonical || languages ? { ...meta, alternates: { ...(canonical ? { canonical } : {}), ...(languages ? { languages } : {}) } } : meta;
|
|
141
151
|
}
|
|
142
152
|
const any = () => true;
|
|
143
153
|
/** The escape hatch may only render ITS OWN registration's record. */
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SMOODLY_VERSION = "0.0.
|
|
1
|
+
export declare const SMOODLY_VERSION = "0.0.40";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/write-version.mjs from package.json — do not edit.
|
|
2
|
-
export const SMOODLY_VERSION = "0.0.
|
|
2
|
+
export const SMOODLY_VERSION = "0.0.40";
|
package/package.json
CHANGED