create-smoodly-app 0.0.29 → 0.0.31

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 CHANGED
@@ -15,8 +15,8 @@ sign in. Spec: `docs/superpowers/specs/2026-09-04-create-smoodly-app-design.md`.
15
15
  `proxy.ts`, translated URLs per locale. Flag: `--locales en,fi`.
16
16
  3. Where content lives: a **local Supabase stack** (`supabase init` +
17
17
  `supabase start` inside the app, with the services Smoodly never uses
18
- excluded — the image proxy, edge functions, analytics, realtime, the
19
- pooler — so the stack stays small beside any other you run; needs
18
+ excluded — the image proxy, edge functions, analytics, the pooler —
19
+ so the stack stays small beside any other you run; needs
20
20
  the Supabase CLI and Docker; when
21
21
  another project holds the default `543xx` ports the new one moves to the
22
22
  next free block, `553xx` and so on, and says so) or a
@@ -82,6 +82,37 @@ Posts and the Site item, a post with a Button element published and
82
82
  served at `/posts/<slug>` in both locales; blank single-language
83
83
  scaffolded in hosted mode against the repo stack and `next build` only.
84
84
 
85
+ ## Follow-ups (logged 2026-09-28, fresh-install proof of 0.0.30)
86
+
87
+ Walked 2026-09-28: `npx create-smoodly-app@0.0.30 proof-0030 --supabase
88
+ local --yes --editor-email …` from the published package, in a scratch
89
+ directory beside three running stacks (the new one took the `563xx`
90
+ block). `smoodly` 0.0.30 installed, `tsc` and `next build` clean, `/`,
91
+ `/posts`, `/posts/<slug>`, `/company/about-us` and `/admin` 200, the
92
+ admin document's `<html lang="en">` present, the editor signed in, an
93
+ SEO title edit saved and published and served on `/`. Found:
94
+
95
+ - **Local mode left Realtime out (0.0.26–0.0.30).** `LOCAL_START_ARGS`
96
+ excluded `realtime` from before the concurrency spec, whose presence
97
+ channel and `saved` events ride it; the admin retried the WebSocket
98
+ forever (a console error per attempt) and a local install had no
99
+ presence or stale-page notice. CI's repo stack already kept Realtime.
100
+ Fixed 2026-09-28: the list no longer excludes it; restarted with the new
101
+ flags, a script signed in as the editor joined `space:<id>` and saw both
102
+ open admin tabs tracked on Home. The live job does not catch this
103
+ (it requests `/admin` once, no browser); an existing local install
104
+ restarts with `supabase stop && supabase start -x imgproxy,edge-runtime,logflare,vector,supavisor`.
105
+ - **The Lorem home's custom-section CTA linked `/articles`**, a 404 —
106
+ the articles collection lives at `/posts`. `examples/site/scripts/seed.ts`
107
+ hardcoded the href; fixed 2026-09-28 to `paths(locale).articles` like
108
+ every other starter link (Finnish gets its own segment too).
109
+ - **`--editor-email` without `SMOODLY_EDITOR_PASSWORD` fails late.** The
110
+ check lives in `askEditorWith`, which runs after the copy, install,
111
+ `git init` and `supabase start`, so the run stops at `ensureEditor`
112
+ with a half-made app. It has to be late for hosted mode (the email may
113
+ name an existing Auth user, no password needed), but a local stack is
114
+ always fresh, so local mode could refuse up front. Not fixed.
115
+
85
116
  ## Follow-ups (logged 2026-09-25, docs walk)
86
117
 
87
118
  - **Single-language `/admin` had no root `<html>` (0.0.15–0.0.20).** The
package/dist/index.js CHANGED
@@ -413,7 +413,7 @@ function parseStatusEnv(output) {
413
413
  }
414
414
 
415
415
  // src/run.ts
416
- var LOCAL_START_ARGS = ["start", "-x", "imgproxy,edge-runtime,logflare,vector,realtime,supavisor"];
416
+ var LOCAL_START_ARGS = ["start", "-x", "imgproxy,edge-runtime,logflare,vector,supavisor"];
417
417
  async function run(answers2, deps) {
418
418
  if (answers2.supabase === "hosted" && !answers2.hosted) {
419
419
  throw new Error("hosted answers need the project's keys");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-smoodly-app",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "description": "Create a Smoodly site: a fresh Next.js app with the admin mounted, a Supabase schema, an env file and a first editor.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -22,8 +22,12 @@ where things are.
22
22
  written through the page and entry stores, never raw SQL. It is
23
23
  idempotent; run it with `npm run seed`.
24
24
  - **Never import the admin into the site.** Nothing under `app/(site)`,
25
- `app/[locale]` or `components/` may import `@/smoodly/admin` or
26
- `smoodly/admin/next`; the admin's chunk must not reach visitors.
25
+ `app/[locale]` or `components/`, and nothing they import
26
+ (`smoodly/server.ts` included), may import `@/smoodly/admin` or
27
+ `smoodly/admin/next`: Next ships every client module a route imports,
28
+ rendered or not, and the admin's chunk must not reach visitors. The
29
+ package's `smoodly/admin` entry has no client code; server-side
30
+ admin glue such as `supabaseAdminAuth` comes from there.
27
31
  - **Two root layouts.** The site's layout lives in its route group or
28
32
  locale segment and is never the admin's parent; `app/(smoodly)/admin`
29
33
  is a second root layout. Do not add an `app/layout.tsx`.
@@ -14,7 +14,7 @@
14
14
  "next": "^16.3.4",
15
15
  "react": "^19",
16
16
  "react-dom": "^19",
17
- "smoodly": "0.0.29"
17
+ "smoodly": "0.0.31"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@next/env": "^16.3.4",
@@ -11,7 +11,10 @@
11
11
  import {
12
12
  createSmoodlyClient, createSmoodlySite, publicContentConfig, smoodlyEnv, SupabaseAssetStore, SupabaseEntryStore, SupabaseMemberStore, SupabasePageStore, SupabasePathIndex, type ContentConfig, type SmoodlySite,
13
13
  } from "smoodly";
14
- import { supabaseAdminAuth, type AdminAuth, type AuthConfig } from "smoodly/admin/next";
14
+ // The admin gate from `smoodly/admin`, which holds no client code: the site
15
+ // routes import this file, and `smoodly/admin/next` would put the admin
16
+ // shell in every live page's JavaScript.
17
+ import { supabaseAdminAuth, type AdminAuth, type AuthConfig } from "smoodly/admin";
15
18
  import { createPageRenderer, createSmoodlyQueries, type PageRegistration } from "smoodly/next";
16
19
  import type { SupabaseClient } from "@supabase/supabase-js";
17
20
  import { collections, config, elements, sections, globals } from "./config";
@@ -22,8 +22,12 @@ where things are.
22
22
  written through the page and entry stores, never raw SQL. It is
23
23
  idempotent; run it with `npm run seed`.
24
24
  - **Never import the admin into the site.** Nothing under `app/(site)`,
25
- `app/[locale]` or `components/` may import `@/smoodly/admin` or
26
- `smoodly/admin/next`; the admin's chunk must not reach visitors.
25
+ `app/[locale]` or `components/`, and nothing they import
26
+ (`smoodly/server.ts` included), may import `@/smoodly/admin` or
27
+ `smoodly/admin/next`: Next ships every client module a route imports,
28
+ rendered or not, and the admin's chunk must not reach visitors. The
29
+ package's `smoodly/admin` entry has no client code; server-side
30
+ admin glue such as `supabaseAdminAuth` comes from there.
27
31
  - **Two root layouts.** The site's layout lives in its route group or
28
32
  locale segment and is never the admin's parent; `app/(smoodly)/admin`
29
33
  is a second root layout. Do not add an `app/layout.tsx`.
@@ -14,7 +14,7 @@
14
14
  "next": "^16.3.4",
15
15
  "react": "^19",
16
16
  "react-dom": "^19",
17
- "smoodly": "0.0.29",
17
+ "smoodly": "0.0.31",
18
18
  "next-intl": "^4.14.2"
19
19
  },
20
20
  "devDependencies": {
@@ -11,7 +11,10 @@
11
11
  import {
12
12
  createSmoodlyClient, createSmoodlySite, publicContentConfig, smoodlyEnv, SupabaseAssetStore, SupabaseEntryStore, SupabaseMemberStore, SupabasePageStore, SupabasePathIndex, type ContentConfig, type SmoodlySite,
13
13
  } from "smoodly";
14
- import { supabaseAdminAuth, type AdminAuth, type AuthConfig } from "smoodly/admin/next";
14
+ // The admin gate from `smoodly/admin`, which holds no client code: the site
15
+ // routes import this file, and `smoodly/admin/next` would put the admin
16
+ // shell in every live page's JavaScript.
17
+ import { supabaseAdminAuth, type AdminAuth, type AuthConfig } from "smoodly/admin";
15
18
  import { createPageRenderer, createSmoodlyQueries, type PageRegistration } from "smoodly/next";
16
19
  import type { SupabaseClient } from "@supabase/supabase-js";
17
20
  import { getPathname } from "@/i18n/navigation";
@@ -22,8 +22,12 @@ where things are.
22
22
  written through the page and entry stores, never raw SQL. It is
23
23
  idempotent; run it with `npm run seed`.
24
24
  - **Never import the admin into the site.** Nothing under `app/(site)`,
25
- `app/[locale]` or `components/` may import `@/smoodly/admin` or
26
- `smoodly/admin/next`; the admin's chunk must not reach visitors.
25
+ `app/[locale]` or `components/`, and nothing they import
26
+ (`smoodly/server.ts` included), may import `@/smoodly/admin` or
27
+ `smoodly/admin/next`: Next ships every client module a route imports,
28
+ rendered or not, and the admin's chunk must not reach visitors. The
29
+ package's `smoodly/admin` entry has no client code; server-side
30
+ admin glue such as `supabaseAdminAuth` comes from there.
27
31
  - **Two root layouts.** The site's layout lives in its route group or
28
32
  locale segment and is never the admin's parent; `app/(smoodly)/admin`
29
33
  is a second root layout. Do not add an `app/layout.tsx`.
@@ -14,7 +14,7 @@
14
14
  "next": "^16.3.4",
15
15
  "react": "^19",
16
16
  "react-dom": "^19",
17
- "smoodly": "0.0.29"
17
+ "smoodly": "0.0.31"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@next/env": "^16.3.4",
@@ -255,7 +255,7 @@ const favourites = (heading: string): Node => ({
255
255
 
256
256
  // The custom section (spec 2026-09-14 §6): the editor's own rows of
257
257
  // richtext columns, widths in twelfths; Smoodly renders its layout CSS.
258
- const customSection = (): Node => ({
258
+ const customSection = (locale: string): Node => ({
259
259
  id: "node-custom",
260
260
  type: "custom",
261
261
  fields: { rows: [
@@ -263,7 +263,7 @@ const customSection = (): Node => ({
263
263
  doc(h2("Sed ut perspiciatis"), p("Unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.")),
264
264
  doc(p("Totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo."), callout("Nemo enim", "Ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit.", "info")),
265
265
  ] },
266
- { spans: [12], gap: "md", align: "top", reverse: false, columns: [doc(cta("Neque porro quisquam", "Qui dolorem ipsum quia dolor sit amet.", "Read the articles", "/articles"))] },
266
+ { spans: [12], gap: "md", align: "top", reverse: false, columns: [doc(cta("Neque porro quisquam", "Qui dolorem ipsum quia dolor sit amet.", "Read the articles", paths(locale).articles))] },
267
267
  ] },
268
268
  styles: { spacingBottom: "lg" },
269
269
  });
@@ -288,7 +288,7 @@ const homeTree = (locale: string, t: { kicker: string; buttons: [string, string]
288
288
  image: img.hero,
289
289
  },
290
290
  }],
291
- body: [imageText(locale, t.link), quote, points, customSection(), favourites(t.favourites)],
291
+ body: [imageText(locale, t.link), quote, points, customSection(locale), favourites(t.favourites)],
292
292
  },
293
293
  });
294
294
 
@@ -11,7 +11,10 @@
11
11
  import {
12
12
  createSmoodlyClient, createSmoodlySite, publicContentConfig, smoodlyEnv, SupabaseAssetStore, SupabaseEntryStore, SupabaseMemberStore, SupabasePageStore, SupabasePathIndex, type ContentConfig, type SmoodlySite,
13
13
  } from "smoodly";
14
- import { supabaseAdminAuth, type AdminAuth, type AuthConfig } from "smoodly/admin/next";
14
+ // The admin gate from `smoodly/admin`, which holds no client code: the site
15
+ // routes import this file, and `smoodly/admin/next` would put the admin
16
+ // shell in every live page's JavaScript.
17
+ import { supabaseAdminAuth, type AdminAuth, type AuthConfig } from "smoodly/admin";
15
18
  import { createPageRenderer, createSmoodlyQueries, type PageRegistration } from "smoodly/next";
16
19
  import type { SupabaseClient } from "@supabase/supabase-js";
17
20
  import { collections, config, elements, sections, globals } from "./config";
@@ -22,8 +22,12 @@ where things are.
22
22
  written through the page and entry stores, never raw SQL. It is
23
23
  idempotent; run it with `npm run seed`.
24
24
  - **Never import the admin into the site.** Nothing under `app/(site)`,
25
- `app/[locale]` or `components/` may import `@/smoodly/admin` or
26
- `smoodly/admin/next`; the admin's chunk must not reach visitors.
25
+ `app/[locale]` or `components/`, and nothing they import
26
+ (`smoodly/server.ts` included), may import `@/smoodly/admin` or
27
+ `smoodly/admin/next`: Next ships every client module a route imports,
28
+ rendered or not, and the admin's chunk must not reach visitors. The
29
+ package's `smoodly/admin` entry has no client code; server-side
30
+ admin glue such as `supabaseAdminAuth` comes from there.
27
31
  - **Two root layouts.** The site's layout lives in its route group or
28
32
  locale segment and is never the admin's parent; `app/(smoodly)/admin`
29
33
  is a second root layout. Do not add an `app/layout.tsx`.
@@ -14,7 +14,7 @@
14
14
  "next": "^16.3.4",
15
15
  "react": "^19",
16
16
  "react-dom": "^19",
17
- "smoodly": "0.0.29",
17
+ "smoodly": "0.0.31",
18
18
  "next-intl": "^4.14.2"
19
19
  },
20
20
  "devDependencies": {
@@ -255,7 +255,7 @@ const favourites = (heading: string): Node => ({
255
255
 
256
256
  // The custom section (spec 2026-09-14 §6): the editor's own rows of
257
257
  // richtext columns, widths in twelfths; Smoodly renders its layout CSS.
258
- const customSection = (): Node => ({
258
+ const customSection = (locale: string): Node => ({
259
259
  id: "node-custom",
260
260
  type: "custom",
261
261
  fields: { rows: [
@@ -263,7 +263,7 @@ const customSection = (): Node => ({
263
263
  doc(h2("Sed ut perspiciatis"), p("Unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.")),
264
264
  doc(p("Totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo."), callout("Nemo enim", "Ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit.", "info")),
265
265
  ] },
266
- { spans: [12], gap: "md", align: "top", reverse: false, columns: [doc(cta("Neque porro quisquam", "Qui dolorem ipsum quia dolor sit amet.", "Read the articles", "/articles"))] },
266
+ { spans: [12], gap: "md", align: "top", reverse: false, columns: [doc(cta("Neque porro quisquam", "Qui dolorem ipsum quia dolor sit amet.", "Read the articles", paths(locale).articles))] },
267
267
  ] },
268
268
  styles: { spacingBottom: "lg" },
269
269
  });
@@ -288,7 +288,7 @@ const homeTree = (locale: string, t: { kicker: string; buttons: [string, string]
288
288
  image: img.hero,
289
289
  },
290
290
  }],
291
- body: [imageText(locale, t.link), quote, points, customSection(), favourites(t.favourites)],
291
+ body: [imageText(locale, t.link), quote, points, customSection(locale), favourites(t.favourites)],
292
292
  },
293
293
  });
294
294
 
@@ -11,7 +11,10 @@
11
11
  import {
12
12
  createSmoodlyClient, createSmoodlySite, publicContentConfig, smoodlyEnv, SupabaseAssetStore, SupabaseEntryStore, SupabaseMemberStore, SupabasePageStore, SupabasePathIndex, type ContentConfig, type SmoodlySite,
13
13
  } from "smoodly";
14
- import { supabaseAdminAuth, type AdminAuth, type AuthConfig } from "smoodly/admin/next";
14
+ // The admin gate from `smoodly/admin`, which holds no client code: the site
15
+ // routes import this file, and `smoodly/admin/next` would put the admin
16
+ // shell in every live page's JavaScript.
17
+ import { supabaseAdminAuth, type AdminAuth, type AuthConfig } from "smoodly/admin";
15
18
  import { createPageRenderer, createSmoodlyQueries, type PageRegistration } from "smoodly/next";
16
19
  import type { SupabaseClient } from "@supabase/supabase-js";
17
20
  import { getPathname } from "@/i18n/navigation";