@clubedge/create-clubedge-app 0.42.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +12 -11
  3. package/dist/core/customize.js +6 -3
  4. package/dist/core/update.js +21 -0
  5. package/dist/modify.js +20 -1
  6. package/package.json +3 -3
  7. package/template/SETUP.md +1 -1
  8. package/template/apps/api/package.json +1 -1
  9. package/template/apps/api-fastapi/package.json +1 -1
  10. package/template/apps/api-fastapi/pyproject.toml +1 -1
  11. package/template/apps/api-fastapi/uv.lock +1 -1
  12. package/template/apps/capacitor/assets/logo.svg +1 -0
  13. package/template/apps/capacitor/package.json +2 -1
  14. package/template/apps/capacitor/scripts/build.mjs +5 -1
  15. package/template/apps/capacitor/scripts/icons.mjs +106 -0
  16. package/template/apps/start/package.json +1 -1
  17. package/template/apps/start/public/.well-known/logo.svg +1 -1
  18. package/template/apps/start/public/apple-touch-icon.png +0 -0
  19. package/template/apps/start/public/favicon.ico +0 -0
  20. package/template/apps/start/public/favicon.svg +1 -0
  21. package/template/apps/start/src/components/auth/auth-page.tsx +1 -1
  22. package/template/apps/start/src/components/dashboard/dashboard-shell.tsx +1 -1
  23. package/template/apps/start/src/routes/__root.tsx +3 -1
  24. package/template/apps/start/src/routes/index.tsx +1 -1
  25. package/template/apps/start/src/routes/login.tsx +1 -1
  26. package/template/apps/vite/index.html +3 -1
  27. package/template/apps/vite/package.json +1 -1
  28. package/template/apps/vite/public/.well-known/logo.svg +1 -1
  29. package/template/apps/vite/public/apple-touch-icon.png +0 -0
  30. package/template/apps/vite/public/favicon.ico +0 -0
  31. package/template/apps/vite/public/favicon.svg +1 -0
  32. package/template/apps/vite/src/components/branding/clubedge-mark.tsx +5 -1
  33. package/template/apps/vite/src/components/dashboard/dashboard-shell.tsx +1 -1
  34. package/template/apps/vite/src/routes/index.tsx +1 -1
  35. package/template/apps/web/package.json +1 -1
  36. package/template/apps/web/public/.well-known/logo.svg +1 -1
  37. package/template/apps/web/src/app/apple-icon.png +0 -0
  38. package/template/apps/web/src/app/dashboard/_components/dashboard-shell.tsx +1 -1
  39. package/template/apps/web/src/app/favicon.ico +0 -0
  40. package/template/apps/web/src/app/icon.svg +1 -0
  41. package/template/apps/web/src/app/login/_components/auth-page.tsx +1 -1
  42. package/template/apps/web/src/app/login/page.tsx +1 -1
  43. package/template/apps/web/src/app/page.tsx +1 -1
  44. package/template/package.json +1 -1
  45. package/template/packages/auth/package.json +1 -1
  46. package/template/packages/auth-appwrite/package.json +1 -1
  47. package/template/packages/auth-better-auth/package.json +1 -1
  48. package/template/packages/auth-firebase/package.json +1 -1
  49. package/template/packages/auth-supabase/package.json +1 -1
  50. package/template/packages/cache/package.json +1 -1
  51. package/template/packages/cache-redis/package.json +1 -1
  52. package/template/packages/contract/package.json +1 -1
  53. package/template/packages/core/package.json +1 -1
  54. package/template/packages/db/package.json +1 -1
  55. package/template/packages/email/package.json +1 -1
  56. package/template/packages/email-resend/package.json +1 -1
  57. package/template/packages/email-smtp/package.json +1 -1
  58. package/template/packages/jobs/package.json +1 -1
  59. package/template/packages/jobs-bullmq/package.json +1 -1
  60. package/template/packages/jobs-inngest/package.json +1 -1
  61. package/template/packages/jobs-pg-boss/package.json +1 -1
  62. package/template/packages/local-db/package.json +1 -1
  63. package/template/packages/mobile/package.json +1 -1
  64. package/template/packages/observability/package.json +1 -1
  65. package/template/packages/observability-otel/package.json +1 -1
  66. package/template/packages/observability-sentry/package.json +1 -1
  67. package/template/packages/payments/package.json +1 -1
  68. package/template/packages/payments-stripe/package.json +1 -1
  69. package/template/packages/storage/package.json +1 -1
  70. package/template/packages/storage-s3/package.json +1 -1
  71. package/template/packages/storage-supabase/package.json +1 -1
  72. package/template/packages/ui/package.json +1 -1
  73. package/template.lock.json +10 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to `create-clubedge-app` are documented here.
4
4
 
5
+ ## 0.43.0
6
+
7
+ - **Upgrades keep each app's own version** (#134, by @ahmed-husssain):
8
+ - An app whose version the project changed in `apps/*/package.json` keeps it, without a conflict on that line.
9
+ - An app whose version the project never touched takes the new Starter's.
10
+ - This completes 0.41.0's version ownership, which covered the root `package.json`.
11
+ - **`add`, `remove`, and `upgrade` that change nothing exit before downloading the Starter** (#133, by @ahmed-husssain; issue #132):
12
+ - The cases: `upgrade` on the pinned Starter's commit or with `--ref` set to the recorded commit, `add` with options the project already uses, and `remove` of modules recorded as `none`.
13
+ - These commands now work offline. They no longer refuse a project with uncommitted changes, since they change nothing.
14
+ - `upgrade --ref <branch>` still resolves the branch, which may have moved. `remove` with an unknown module still fails with `Unknown module`.
15
+ - Still on Starter `v0.27.1`.
16
+
17
+ ## 0.42.1
18
+
19
+ - Default to Starter `v0.27.1` (#140): new projects carry the Clubedge Starter brand, and their mobile app shows it.
20
+ - Each web app's logo is the new mark, shown whole: the pages no longer round its corners. Each app also has `favicon.ico`, a new `favicon.svg`, and an Apple touch icon.
21
+ - The mobile app shows the logo on its pages. The logo was in a `.well-known` folder, which Android leaves out of an app, so the Vite app now bundles it.
22
+ - The mobile app's icons and splash screens are made from `apps/shell/assets/logo.svg` instead of Capacitor's default.
23
+ - Upgrading: a logo you replaced comes back as a conflict in `logo.svg`, so keep yours. The [Starter's release notes](https://github.com/Clubedge/clubedge-starter/releases/tag/v0.27.1) cover replaced favicons and existing `android/` and `ios/` projects.
24
+
5
25
  ## 0.42.0
6
26
 
7
27
  - **Mobile apps with Capacitor**: default to Starter `v0.27.0` (#138). `--framework vite --api hono --shell capacitor` (or `--api fastapi`) packages the Vite frontend as an Android and iOS app in `apps/shell` (#135):
package/README.md CHANGED
@@ -223,7 +223,7 @@ pnpm dlx @clubedge/create-clubedge-app my-app --yes --no-git --framework tanstac
223
223
  pnpm dlx @clubedge/create-clubedge-app my-app --ref main
224
224
 
225
225
  # Reproduce a specific Starter release or commit
226
- pnpm dlx @clubedge/create-clubedge-app my-app --ref v0.27.0
226
+ pnpm dlx @clubedge/create-clubedge-app my-app --ref v0.27.1
227
227
  pnpm dlx @clubedge/create-clubedge-app my-app --ref <commit-sha>
228
228
 
229
229
  # Test local Starter changes before releasing them
@@ -242,7 +242,7 @@ pnpm clubedge add email resend payments stripe # several modules as
242
242
  pnpm clubedge remove storage payments
243
243
  pnpm dlx @clubedge/create-clubedge-app add infra local --dry-run
244
244
  pnpm dlx @clubedge/create-clubedge-app@latest upgrade # to the newest CLI's Starter release
245
- pnpm dlx @clubedge/create-clubedge-app upgrade --ref v0.27.0 # or a specific tag, branch, or commit
245
+ pnpm dlx @clubedge/create-clubedge-app upgrade --ref v0.27.1 # or a specific tag, branch, or commit
246
246
  ```
247
247
 
248
248
  The CLI renders the project twice, from the Starter commit and modules recorded in your `package.json` and from the new state, and merges the difference into your project. The new state has other modules for `add` and `remove`, and the newer Starter with the same modules for `upgrade`. Modules a newer Starter introduces start as `none` where possible, so an upgrade never adds a service on its own.
@@ -358,7 +358,7 @@ docker compose --env-file .env.production run --rm web node /app/migrate.mjs #
358
358
  docker compose --env-file .env.production up -d # --build builds the images on the server
359
359
  ```
360
360
 
361
- `deploy` prints these steps, with the `IMAGE_PREFIX` of your repository when `origin` is on GitHub. To update, publish a release, then on the server `docker compose --env-file .env.production pull` (or set `TAG` to the new version), run the migrations, and `up -d` again. Running `deploy` again writes the files that are missing and never overwrites one: it keeps a `Caddyfile` or workflow you changed, and says so, and stops when `compose.yaml` differs from the one it writes, so move yours away to get the new one. Compose prefers `compose.yaml` to the Starter's `docker-compose.yml`, so run the local one by name: `docker compose -f docker-compose.yml up --build`, which `pnpm docker:dev` does since Starter v0.27.0.
361
+ `deploy` prints these steps, with the `IMAGE_PREFIX` of your repository when `origin` is on GitHub. To update, publish a release, then on the server `docker compose --env-file .env.production pull` (or set `TAG` to the new version), run the migrations, and `up -d` again. Running `deploy` again writes the files that are missing and never overwrites one: it keeps a `Caddyfile` or workflow you changed, and says so, and stops when `compose.yaml` differs from the one it writes, so move yours away to get the new one. Compose prefers `compose.yaml` to the Starter's `docker-compose.yml`, so run the local one by name: `docker compose -f docker-compose.yml up --build`, which `pnpm docker:dev` does since Starter v0.27.1.
362
362
 
363
363
  ### Client addresses
364
364
 
@@ -381,27 +381,27 @@ Projects created with `--infra neon` also have `.github/workflows/preview.yml`,
381
381
  Each CLI release is tested against one specific Starter release:
382
382
 
383
383
  ```text
384
- create-clubedge-app v0.42.0
384
+ create-clubedge-app v0.43.0
385
385
  |
386
386
  v
387
- Clubedge Starter v0.27.0
388
- commit 5f0e7e57131c4c56305d67edfd2e7366618d7b7c
387
+ Clubedge Starter v0.27.1
388
+ commit 997d71d74775352d30dc2bb25017f46ef43f4fd8
389
389
  ```
390
390
 
391
391
  The current relationship is:
392
392
 
393
393
  | CLI release | Default Starter release | Meaning |
394
394
  | --- | --- | --- |
395
- | `create-clubedge-app@0.42.0` | `clubedge-starter@v0.27.0` | The CLI's required, tested default |
395
+ | `create-clubedge-app@0.43.0` | `clubedge-starter@v0.27.1` | The CLI's required, tested default |
396
396
 
397
397
  This means the same CLI version always scaffolds the same Starter revision by default. The CLI does not silently follow Starter `main`.
398
398
 
399
- The `v0.27.0` tag is expected to resolve to commit
400
- `5f0e7e57131c4c56305d67edfd2e7366618d7b7c`. The CLI verifies this before
399
+ The `v0.27.1` tag is expected to resolve to commit
400
+ `997d71d74775352d30dc2bb25017f46ef43f4fd8`. The CLI verifies this before
401
401
  scaffolding with the default reference and fails rather than silently using a
402
402
  moved tag.
403
403
 
404
- When the Starter changes, its maintainers first publish a new versioned release, such as `v0.27.0`. The CLI is then updated to use that release, all generated-project checks must pass, and a new CLI version is published. This keeps Starter and CLI releases independently versioned while preserving a tested compatibility relationship.
404
+ When the Starter changes, its maintainers first publish a new versioned release, such as `v0.27.1`. The CLI is then updated to use that release, all generated-project checks must pass, and a new CLI version is published. This keeps Starter and CLI releases independently versioned while preserving a tested compatibility relationship.
405
405
 
406
406
  Every generated project records its CLI version, Starter repository, Starter ref, Starter commit, framework, and modules in `package.json` and `README.md`.
407
407
 
@@ -456,7 +456,7 @@ CI packs the CLI with `npm pack`, scaffolds clean projects from the packed tarba
456
456
  1. Make and validate changes in `clubedge-starter`.
457
457
  2. Run lint, typecheck, unit tests, browser tests, and production build.
458
458
  3. Update the Starter package versions.
459
- 4. Commit `main`, create a release tag such as `v0.27.0`, and publish the Starter release.
459
+ 4. Commit `main`, create a release tag such as `v0.27.1`, and publish the Starter release.
460
460
 
461
461
  ### CLI maintainers
462
462
 
@@ -509,6 +509,7 @@ Each phase ends with a Starter release and a CLI release pinned to it. The full
509
509
  | 0.40.0 | v0.26.0 | Starter v0.26.0: every module on React + Vite with FastAPI (pg-boss excepted) |
510
510
  | 0.41.0 | v0.26.0 | A conflicted package.json no longer breaks later commands (`--detailed-exit-code`); doctor finds conflict markers; projects own their version and README |
511
511
  | 0.42.0 | v0.27.0 | Starter v0.27.0: mobile apps with Capacitor (`--shell capacitor`, template schema 5); client addresses per platform; Docker Compose behind Caddy, with images on GHCR |
512
+ | 0.43.0 | v0.27.1 | Upgrades keep each app's own version; add, remove, and upgrade that change nothing skip the Starter download |
512
513
 
513
514
  ## Community
514
515
 
@@ -88,10 +88,13 @@ export function customizeLegacySource(source, { displayName }) {
88
88
  .replaceAll(">Clubedge<", `>${displayName}<`)
89
89
  .replaceAll("Starter workspace", "Application workspace");
90
90
  }
91
- /** Gives a selected stack's app the package name generated projects use. */
92
- export function customizeAppManifest(source, appPackage) {
91
+ /** Gives a selected stack's app the package name generated projects use and preserves its version. */
92
+ export function customizeAppManifest(source, appPackage, version) {
93
93
  const manifest = JSON.parse(source);
94
- manifest.name = appPackage;
94
+ if (appPackage)
95
+ manifest.name = appPackage;
96
+ if (version !== undefined)
97
+ manifest.version = version;
95
98
  return json(manifest);
96
99
  }
97
100
  /**
@@ -3,6 +3,7 @@
3
3
  // through a three-way merge, see merge.ts.
4
4
  import { mkdir, readdir, rm, rmdir, writeFile } from "node:fs/promises";
5
5
  import { dirname, join } from "node:path";
6
+ import { customizeAppManifest } from "./customize.js";
6
7
  import { updateEnvFile } from "./env.js";
7
8
  import { INCOMING_DIR, planMerge, sameContent } from "./merge.js";
8
9
  import { readProjectFiles, renderProject } from "./project.js";
@@ -35,6 +36,26 @@ export async function planUpdate(project, base, target) {
35
36
  baseFiles.delete("README.md");
36
37
  targetFiles.delete("README.md");
37
38
  }
39
+ // Workspace apps take the new Starter's version unless the project customized their version.
40
+ for (const [path, targetContent] of targetFiles) {
41
+ if (!path.startsWith("apps/") || !path.endsWith("/package.json"))
42
+ continue;
43
+ const baseContent = baseFiles.get(path);
44
+ const currentContent = current.get(path);
45
+ if (!baseContent || !currentContent)
46
+ continue;
47
+ try {
48
+ const basePkg = JSON.parse(baseContent.toString("utf8"));
49
+ const currentPkg = JSON.parse(currentContent.toString("utf8"));
50
+ if (typeof currentPkg.version === "string" && currentPkg.version !== basePkg.version) {
51
+ baseFiles.set(path, Buffer.from(customizeAppManifest(baseContent.toString("utf8"), undefined, currentPkg.version)));
52
+ targetFiles.set(path, Buffer.from(customizeAppManifest(targetContent.toString("utf8"), undefined, currentPkg.version)));
53
+ }
54
+ }
55
+ catch {
56
+ // Leave unparseable manifests untouched.
57
+ }
58
+ }
38
59
  const actions = planMerge(baseFiles, targetFiles, current);
39
60
  if (customReadme && targetReadme && !sameContent(currentReadme, targetReadme)) {
40
61
  actions.push({
package/dist/modify.js CHANGED
@@ -366,8 +366,27 @@ export async function changeProject(args) {
366
366
  throw new Error("Not a project change.");
367
367
  const interactive = Boolean(process.stdin.isTTY) && !args.yes;
368
368
  const project = await readProject(process.cwd());
369
- await assertSafeToChange(project, args, interactive);
370
369
  const upgrading = command.name === "upgrade";
370
+ if (upgrading &&
371
+ !args.templateDir &&
372
+ project.starterCommit &&
373
+ (args.ref ? args.ref === project.starterCommit : project.starterCommit === starterPin.starterCommit)) {
374
+ outro(`The project is already on Starter ${project.starterRef}.`);
375
+ return "unchanged";
376
+ }
377
+ if (command.name === "add" &&
378
+ command.changes.length > 0 &&
379
+ command.changes.every((c) => c.option && project.modules[c.module] === c.option)) {
380
+ outro("The project already uses that selection; nothing to change.");
381
+ return "unchanged";
382
+ }
383
+ if (command.name === "remove" &&
384
+ command.modules.length > 0 &&
385
+ command.modules.every((m) => project.modules[m] === "none")) {
386
+ outro("The project already uses that selection; nothing to change.");
387
+ return "unchanged";
388
+ }
389
+ await assertSafeToChange(project, args, interactive);
371
390
  const progress = spinner();
372
391
  progress.start(upgrading ? "Preparing both Starter revisions" : `Preparing Starter ${project.starterRef}`);
373
392
  const sources = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/create-clubedge-app",
3
- "version": "0.42.0",
3
+ "version": "0.43.0",
4
4
  "description": "Create React apps from Clubedge Starter with a CLI or local MCP server.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.9.0",
@@ -24,8 +24,8 @@
24
24
  },
25
25
  "clubedge": {
26
26
  "starterRepository": "Clubedge/clubedge-starter",
27
- "starterRef": "v0.27.0",
28
- "starterCommit": "5f0e7e57131c4c56305d67edfd2e7366618d7b7c"
27
+ "starterRef": "v0.27.1",
28
+ "starterCommit": "997d71d74775352d30dc2bb25017f46ef43f4fd8"
29
29
  },
30
30
  "license": "Apache-2.0",
31
31
  "repository": {
package/template/SETUP.md CHANGED
@@ -748,7 +748,7 @@ Installed apps keep calling the contract they shipped with, so change `openapi.y
748
748
 
749
749
  1. Set your app's id in `apps/shell/capacitor.config.ts`: a reverse-domain name you own, such as `com.yourcompany.app`. It becomes permanent once the Android and iOS projects exist and a store lists the app, so set it before the first build. The app's name comes from `apps/web/src/config/site.json`.
750
750
  2. Set `WEB_URL` in `apps/shell/.env.local` to the website's public `https://` address, such as `https://app.example.com`. The build refuses any other: the app sends its session there.
751
- 3. Run `pnpm --filter @clubedge/shell android` (or `ios`). It builds the web app for the app, creates `apps/shell/android` (or `apps/shell/ios`) on the first run, copies the build into it, and opens Android Studio (or Xcode), where you run the app on a device or an emulator. Commit `android/` and `ios/`: from then on they are your app's own, with its icons, permissions, and signing settings.
751
+ 3. Run `pnpm --filter @clubedge/shell android` (or `ios`). It builds the web app for the app, creates `apps/shell/android` (or `apps/shell/ios`) on the first run, copies the build into it, and opens Android Studio (or Xcode), where you run the app on a device or an emulator. Commit `android/` and `ios/`: from then on they are your app's own, with its icons, permissions, and signing settings. The first run also makes the app's icons and splash screens from `apps/shell/assets/logo.svg`, the white mark on the brand's blue square, with Capacitor's asset tool (run through npx, so it needs the network then); after changing it, run `pnpm --filter @clubedge/shell icons`, and set the backgrounds to match it at the top of `apps/shell/scripts/icons.mjs`.
752
752
 
753
753
  After changing the web app, `pnpm --filter @clubedge/shell sync` copies a new build into the native projects. Release builds and store listings are made in Android Studio and Xcode, as Capacitor's guides for [Google Play](https://capacitorjs.com/docs/android/deploying-to-google-play) and the [App Store](https://capacitorjs.com/docs/ios/deploying-to-app-store) describe. The project's CI builds a debug APK too; set a `WEB_URL` repository variable so that it calls your website.
754
754
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/api",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/api-fastapi",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "clubedge-api"
3
- version = "0.27.0"
3
+ version = "0.27.1"
4
4
  description = "The project's API, implementing packages/contract/openapi.yaml."
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [
@@ -304,7 +304,7 @@ wheels = [
304
304
 
305
305
  [[package]]
306
306
  name = "clubedge-api"
307
- version = "0.27.0"
307
+ version = "0.27.1"
308
308
  source = { editable = "." }
309
309
  default-groups = "all"
310
310
  dependencies = [
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512" role="img" aria-label="Clubedge Starter"><title>Clubedge Starter</title><rect width="512" height="512" fill="#2563EB"/><g fill="#FFFFFF"><rect x="138.24" y="138.24" width="67.291" height="67.291" rx="12.617"/><rect x="222.354" y="138.24" width="67.291" height="67.291" rx="12.617"/><rect x="138.24" y="222.354" width="67.291" height="67.291" rx="12.617"/><rect x="138.24" y="306.468" width="67.291" height="67.291" rx="12.617"/><rect x="222.354" y="306.468" width="67.291" height="67.291" rx="12.617"/><circle cx="340.114" cy="171.886" r="33.645"/><circle cx="340.114" cy="340.114" r="33.645"/></g></svg>
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@clubedge/capacitor",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "android": "node --env-file-if-exists=.env.local scripts/build.mjs android --open",
9
+ "icons": "node scripts/icons.mjs",
9
10
  "ios": "node --env-file-if-exists=.env.local scripts/build.mjs ios --open",
10
11
  "sync": "node --env-file-if-exists=.env.local scripts/build.mjs",
11
12
  "typecheck": "tsc --noEmit"
@@ -11,6 +11,7 @@ import { spawnSync } from "node:child_process";
11
11
  import { existsSync } from "node:fs";
12
12
  import { join } from "node:path";
13
13
  import { pathToFileURL } from "node:url";
14
+ import { makeIcons } from "./icons.mjs";
14
15
 
15
16
  const shellDirectory = join(import.meta.dirname, "..");
16
17
  const webDirectory = join(import.meta.dirname, "../../web");
@@ -87,7 +88,10 @@ function build(args) {
87
88
  const capacitor = join(shellDirectory, "node_modules/@capacitor/cli/bin/capacitor");
88
89
  for (const platform of targets) {
89
90
  // cap add creates the native project and copies the build into it; cap sync updates both.
90
- run(capacitor, [exists(platform) ? "sync" : "add", platform], { cwd: shellDirectory });
91
+ // A new project gets its icons and splash screens from the logo (icons.mjs).
92
+ const created = !exists(platform);
93
+ run(capacitor, [created ? "add" : "sync", platform], { cwd: shellDirectory });
94
+ if (created) makeIcons(platform);
91
95
  }
92
96
  if (options.includes("--open")) {
93
97
  for (const platform of targets) run(capacitor, ["open", platform], { cwd: shellDirectory });
@@ -0,0 +1,106 @@
1
+ // The app's icons and splash screens, made from assets/logo.svg by Capacitor's asset tool:
2
+ //
3
+ // node scripts/icons.mjs [android] [ios]
4
+ //
5
+ // build.mjs runs it when it creates a native project; run it again (pnpm icons) after changing
6
+ // the art. Without a platform, it makes them for the native projects that exist. The tool runs
7
+ // through npx at a fixed version, so the project installs nothing for it; it needs the network
8
+ // the first time.
9
+ import { spawnSync } from "node:child_process";
10
+ import { existsSync, readdirSync, statSync } from "node:fs";
11
+ import { join, relative } from "node:path";
12
+ import { pathToFileURL } from "node:url";
13
+
14
+ const shellDirectory = join(import.meta.dirname, "..");
15
+ const logoDirectory = join(import.meta.dirname, "../assets");
16
+ const platforms = ["android", "ios"];
17
+
18
+ // assets/logo.svg is the white mark on a full blue square, so the icon's background is the same
19
+ // blue and the icon fills; the splash screens show it as a tile on the app's light and dark
20
+ // backgrounds. Match these to your own art.
21
+ const iconBackground = "#2563eb";
22
+ const splashBackground = "#ffffff";
23
+ const splashBackgroundDark = "#151515";
24
+
25
+ /** The asset tool's arguments for one platform's icons and splash screens. */
26
+ export function iconArguments(platform) {
27
+ return [
28
+ "--yes",
29
+ "@capacitor/assets@3.0.5",
30
+ "generate",
31
+ `--${platform}`,
32
+ "--assetPath",
33
+ relative(shellDirectory, logoDirectory),
34
+ "--iconBackgroundColor",
35
+ iconBackground,
36
+ "--iconBackgroundColorDark",
37
+ iconBackground,
38
+ "--splashBackgroundColor",
39
+ splashBackground,
40
+ "--splashBackgroundColorDark",
41
+ splashBackgroundDark,
42
+ ];
43
+ }
44
+
45
+ // Where each native project keeps its app icons.
46
+ const iconDirectories = {
47
+ android: ["android/app/src/main/res", (name) => name.startsWith("mipmap")],
48
+ ios: ["ios/App/App/Assets.xcassets", (name) => name === "AppIcon.appiconset"],
49
+ };
50
+
51
+ /** When the platform's icon files last changed, so a run that wrote none shows. */
52
+ function iconsChangedAt(platform) {
53
+ const [directory, isIconFolder] = iconDirectories[platform];
54
+ const root = join(shellDirectory, directory);
55
+ if (!existsSync(root)) return 0;
56
+ let latest = 0;
57
+ for (const folder of readdirSync(root).filter(isIconFolder)) {
58
+ for (const file of readdirSync(join(root, folder))) {
59
+ latest = Math.max(latest, statSync(join(root, folder, file)).mtimeMs);
60
+ }
61
+ }
62
+ return latest;
63
+ }
64
+
65
+ /**
66
+ * Makes one platform's icons and splash screens. When it cannot, as offline, the app keeps
67
+ * Capacitor's own: it says so and returns false, rather than stopping a build. The asset tool
68
+ * skips a platform without a native project and still succeeds, so success also means that it
69
+ * wrote the platform's icons.
70
+ */
71
+ export function makeIcons(platform) {
72
+ if (!existsSync(join(shellDirectory, platform))) {
73
+ console.error(`The app has no ${platform} project yet: run pnpm ${platform} to create it.`);
74
+ return false;
75
+ }
76
+ const before = iconsChangedAt(platform);
77
+ const windows = process.platform === "win32";
78
+ const { status } = spawnSync(windows ? "npx.cmd" : "npx", iconArguments(platform), {
79
+ cwd: shellDirectory,
80
+ stdio: "inherit",
81
+ shell: windows,
82
+ });
83
+ if (status === 0 && iconsChangedAt(platform) > before) return true;
84
+ console.warn(
85
+ `The ${platform} app keeps Capacitor's default icon for now: run pnpm icons to make it from the logo.`,
86
+ );
87
+ return false;
88
+ }
89
+
90
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
91
+ const named = process.argv.slice(2);
92
+ const unknown = named.find((arg) => !platforms.includes(arg));
93
+ if (unknown) {
94
+ console.error(`Unknown platform "${unknown}": choose android or ios.`);
95
+ process.exit(1);
96
+ }
97
+ const targets =
98
+ named.length > 0
99
+ ? named
100
+ : platforms.filter((platform) => existsSync(join(shellDirectory, platform)));
101
+ if (targets.length === 0) {
102
+ console.error("The app has no native project yet: run pnpm android or pnpm ios to create one.");
103
+ process.exit(1);
104
+ }
105
+ if (!targets.map(makeIcons).every(Boolean)) process.exit(1);
106
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/start",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1 +1 @@
1
- <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#2563eb;}.cls-2,.cls-3{fill:#fff;}.cls-2{fill-rule:evenodd;}</style></defs><rect class="cls-1" width="512" height="512"/><path class="cls-2" d="M364.36,283a359.51,359.51,0,0,0-43.5-10.18,364.29,364.29,0,0,1,5.24-55.17l30-15.13,20.83,5.63v34.6A43.79,43.79,0,0,0,364.36,283Zm-66.88-13.4a395.53,395.53,0,0,0-82.94,0,390.43,390.43,0,0,0-3-39L256,253l44.45-22.42A390.43,390.43,0,0,0,297.48,269.6Zm-106.32,3.22c-39.75,6.75-76,19.56-106.55,36.9a175.47,175.47,0,0,1-2.52-29.64,173.14,173.14,0,0,1,31-99.15l72.81,36.72a364.29,364.29,0,0,1,5.24,55.17Zm230.41,60.63A174.34,174.34,0,0,1,352.17,425c-17.72-36.5-28.9-80.53-31-128.34,37.9,6.89,72.13,19.67,100.42,36.78Zm-89.78,103.2a174.19,174.19,0,0,1-151.57,0c20-40.75,32.48-90.09,34.24-143.46a373.23,373.23,0,0,1,83.1,0C299.32,346.56,311.75,395.9,331.79,436.65ZM159.85,425a174.36,174.36,0,0,1-69.41-91.56c28.3-17.11,62.53-29.89,100.42-36.78-2.11,47.81-13.3,91.84-31,128.34Z"/><path class="cls-2" d="M256,58l173,84.17-33.81,17.05L280.05,128.07c-6.92-4.44-16-6.2-24-6.2-7.37,0-15.32,1.45-21.88,4.94s-11.85,9.19-11.85,16.9,5.42,13.48,11.85,16.9,14.51,4.94,21.88,4.94a54.52,54.52,0,0,0,9.29-.78l77.41,20.93L256,229.42,83,142.17,256,58Z"/><path class="cls-2" d="M256,130.18c8.43,0,15.9,2.18,20.52,5.55l140,37.84v72.75a29.68,29.68,0,0,0-18.51,0V192l-132.36-35.8a45,45,0,0,1-9.6,1c-14,0-25.42-6.05-25.42-13.53S242,130.18,256,130.18Z"/><circle class="cls-3" cx="407.23" cy="274.28" r="22.68"/></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="56" height="56" viewBox="0 0 56 56" role="img" aria-label="Clubedge Starter"><title>Clubedge Starter</title><g fill="#2563EB"><rect x="0" y="0" width="16" height="16" rx="3"/><rect x="20" y="0" width="16" height="16" rx="3"/><rect x="0" y="20" width="16" height="16" rx="3"/><rect x="0" y="40" width="16" height="16" rx="3"/><rect x="20" y="40" width="16" height="16" rx="3"/><circle cx="48" cy="8" r="8"/><circle cx="48" cy="48" r="8"/></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60" role="img" aria-label="Clubedge Starter"><title>Clubedge Starter</title><g fill="#2563EB"><rect x="2" y="2" width="16" height="16" rx="3"/><rect x="22" y="2" width="16" height="16" rx="3"/><rect x="2" y="22" width="16" height="16" rx="3"/><rect x="2" y="42" width="16" height="16" rx="3"/><rect x="22" y="42" width="16" height="16" rx="3"/><circle cx="50" cy="10" r="8"/><circle cx="50" cy="50" r="8"/></g></svg>
@@ -24,7 +24,7 @@ export function AuthPage({
24
24
  className="rounded-xl outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
25
25
  to="/"
26
26
  >
27
- <ClubedgeMark className="size-12 rounded-xl" />
27
+ <ClubedgeMark className="size-12" />
28
28
  </Link>
29
29
  <div className="grid gap-1.5">
30
30
  <h1 className="text-2xl font-semibold tracking-tight">{title}</h1>
@@ -176,7 +176,7 @@ function Title({ className }: { className?: string }) {
176
176
  href="#overview"
177
177
  className={cn("inline-flex min-w-0 items-center gap-2.5 font-semibold", className)}
178
178
  >
179
- <ClubedgeMark alt="" className="size-6 shrink-0 rounded-md" />
179
+ <ClubedgeMark alt="" className="size-6 shrink-0" />
180
180
  <span className="truncate">{siteConfig.shortName}</span>
181
181
  </a>
182
182
  );
@@ -30,7 +30,9 @@ export const Route = createRootRoute({
30
30
  ],
31
31
  links: [
32
32
  { rel: "stylesheet", href: appCss },
33
- { rel: "icon", href: "/favicon.ico" },
33
+ { rel: "icon", href: "/favicon.ico", sizes: "48x48" },
34
+ { rel: "icon", href: "/favicon.svg", type: "image/svg+xml" },
35
+ { rel: "apple-touch-icon", href: "/apple-touch-icon.png" },
34
36
  ],
35
37
  scripts: [{ children: themeBootstrap }],
36
38
  }),
@@ -64,7 +64,7 @@ function LandingPage() {
64
64
  className="flex shrink-0 items-center gap-2.5 rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring"
65
65
  to="/"
66
66
  >
67
- <ClubedgeMark alt="" className="size-9 rounded-xl" priority />
67
+ <ClubedgeMark alt="" className="size-9" priority />
68
68
  <span className="hidden text-sm font-semibold tracking-tight min-[380px]:inline">
69
69
  {siteConfig.name}
70
70
  </span>
@@ -71,7 +71,7 @@ function LoginPage() {
71
71
  className="rounded-xl outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
72
72
  to="/"
73
73
  >
74
- <ClubedgeMark className="size-12 rounded-xl" />
74
+ <ClubedgeMark className="size-12" />
75
75
  </Link>
76
76
  <div className="grid gap-1.5">
77
77
  <h1 className="text-2xl font-semibold tracking-tight">
@@ -7,7 +7,9 @@
7
7
  <!-- vite.config.ts fills in the name and description from src/config/site.json. -->
8
8
  <title>%SITE_NAME%</title>
9
9
  <meta name="description" content="%SITE_DESCRIPTION%" />
10
- <link rel="icon" href="/favicon.ico" />
10
+ <link rel="icon" href="/favicon.ico" sizes="48x48" />
11
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
12
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
11
13
  <!-- Applies the saved or preferred theme before first paint to avoid a flash. -->
12
14
  <script>
13
15
  try {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/vite",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1 +1 @@
1
- <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#2563eb;}.cls-2,.cls-3{fill:#fff;}.cls-2{fill-rule:evenodd;}</style></defs><rect class="cls-1" width="512" height="512"/><path class="cls-2" d="M364.36,283a359.51,359.51,0,0,0-43.5-10.18,364.29,364.29,0,0,1,5.24-55.17l30-15.13,20.83,5.63v34.6A43.79,43.79,0,0,0,364.36,283Zm-66.88-13.4a395.53,395.53,0,0,0-82.94,0,390.43,390.43,0,0,0-3-39L256,253l44.45-22.42A390.43,390.43,0,0,0,297.48,269.6Zm-106.32,3.22c-39.75,6.75-76,19.56-106.55,36.9a175.47,175.47,0,0,1-2.52-29.64,173.14,173.14,0,0,1,31-99.15l72.81,36.72a364.29,364.29,0,0,1,5.24,55.17Zm230.41,60.63A174.34,174.34,0,0,1,352.17,425c-17.72-36.5-28.9-80.53-31-128.34,37.9,6.89,72.13,19.67,100.42,36.78Zm-89.78,103.2a174.19,174.19,0,0,1-151.57,0c20-40.75,32.48-90.09,34.24-143.46a373.23,373.23,0,0,1,83.1,0C299.32,346.56,311.75,395.9,331.79,436.65ZM159.85,425a174.36,174.36,0,0,1-69.41-91.56c28.3-17.11,62.53-29.89,100.42-36.78-2.11,47.81-13.3,91.84-31,128.34Z"/><path class="cls-2" d="M256,58l173,84.17-33.81,17.05L280.05,128.07c-6.92-4.44-16-6.2-24-6.2-7.37,0-15.32,1.45-21.88,4.94s-11.85,9.19-11.85,16.9,5.42,13.48,11.85,16.9,14.51,4.94,21.88,4.94a54.52,54.52,0,0,0,9.29-.78l77.41,20.93L256,229.42,83,142.17,256,58Z"/><path class="cls-2" d="M256,130.18c8.43,0,15.9,2.18,20.52,5.55l140,37.84v72.75a29.68,29.68,0,0,0-18.51,0V192l-132.36-35.8a45,45,0,0,1-9.6,1c-14,0-25.42-6.05-25.42-13.53S242,130.18,256,130.18Z"/><circle class="cls-3" cx="407.23" cy="274.28" r="22.68"/></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="56" height="56" viewBox="0 0 56 56" role="img" aria-label="Clubedge Starter"><title>Clubedge Starter</title><g fill="#2563EB"><rect x="0" y="0" width="16" height="16" rx="3"/><rect x="20" y="0" width="16" height="16" rx="3"/><rect x="0" y="20" width="16" height="16" rx="3"/><rect x="0" y="40" width="16" height="16" rx="3"/><rect x="20" y="40" width="16" height="16" rx="3"/><circle cx="48" cy="8" r="8"/><circle cx="48" cy="48" r="8"/></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60" role="img" aria-label="Clubedge Starter"><title>Clubedge Starter</title><g fill="#2563EB"><rect x="2" y="2" width="16" height="16" rx="3"/><rect x="22" y="2" width="16" height="16" rx="3"/><rect x="2" y="22" width="16" height="16" rx="3"/><rect x="2" y="42" width="16" height="16" rx="3"/><rect x="22" y="42" width="16" height="16" rx="3"/><circle cx="50" cy="10" r="8"/><circle cx="50" cy="50" r="8"/></g></svg>
@@ -1,3 +1,7 @@
1
+ // Imported, so the build carries the logo in its own files: a folder whose name starts with a dot,
2
+ // such as .well-known, never reaches the mobile app, since Android leaves those out of an app.
3
+ import logo from "../../../public/.well-known/logo.svg";
4
+
1
5
  type ClubedgeMarkProps = {
2
6
  alt?: string;
3
7
  className?: string;
@@ -17,7 +21,7 @@ export function ClubedgeMark({
17
21
  fetchPriority={priority ? "high" : "auto"}
18
22
  height={512}
19
23
  loading={priority ? "eager" : "lazy"}
20
- src="/.well-known/logo.svg"
24
+ src={logo}
21
25
  width={512}
22
26
  />
23
27
  );
@@ -176,7 +176,7 @@ function Title({ className }: { className?: string }) {
176
176
  href="#overview"
177
177
  className={cn("inline-flex min-w-0 items-center gap-2.5 font-semibold", className)}
178
178
  >
179
- <ClubedgeMark alt="" className="size-6 shrink-0 rounded-md" />
179
+ <ClubedgeMark alt="" className="size-6 shrink-0" />
180
180
  <span className="truncate">{siteConfig.shortName}</span>
181
181
  </a>
182
182
  );
@@ -54,7 +54,7 @@ export function LandingPage() {
54
54
  className="flex shrink-0 items-center gap-2.5 rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring"
55
55
  to="/"
56
56
  >
57
- <ClubedgeMark alt="" className="size-9 rounded-xl" priority />
57
+ <ClubedgeMark alt="" className="size-9" priority />
58
58
  <span className="hidden text-sm font-semibold tracking-tight min-[380px]:inline">
59
59
  {siteConfig.name}
60
60
  </span>
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/web",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {
@@ -1 +1 @@
1
- <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#2563eb;}.cls-2,.cls-3{fill:#fff;}.cls-2{fill-rule:evenodd;}</style></defs><rect class="cls-1" width="512" height="512"/><path class="cls-2" d="M364.36,283a359.51,359.51,0,0,0-43.5-10.18,364.29,364.29,0,0,1,5.24-55.17l30-15.13,20.83,5.63v34.6A43.79,43.79,0,0,0,364.36,283Zm-66.88-13.4a395.53,395.53,0,0,0-82.94,0,390.43,390.43,0,0,0-3-39L256,253l44.45-22.42A390.43,390.43,0,0,0,297.48,269.6Zm-106.32,3.22c-39.75,6.75-76,19.56-106.55,36.9a175.47,175.47,0,0,1-2.52-29.64,173.14,173.14,0,0,1,31-99.15l72.81,36.72a364.29,364.29,0,0,1,5.24,55.17Zm230.41,60.63A174.34,174.34,0,0,1,352.17,425c-17.72-36.5-28.9-80.53-31-128.34,37.9,6.89,72.13,19.67,100.42,36.78Zm-89.78,103.2a174.19,174.19,0,0,1-151.57,0c20-40.75,32.48-90.09,34.24-143.46a373.23,373.23,0,0,1,83.1,0C299.32,346.56,311.75,395.9,331.79,436.65ZM159.85,425a174.36,174.36,0,0,1-69.41-91.56c28.3-17.11,62.53-29.89,100.42-36.78-2.11,47.81-13.3,91.84-31,128.34Z"/><path class="cls-2" d="M256,58l173,84.17-33.81,17.05L280.05,128.07c-6.92-4.44-16-6.2-24-6.2-7.37,0-15.32,1.45-21.88,4.94s-11.85,9.19-11.85,16.9,5.42,13.48,11.85,16.9,14.51,4.94,21.88,4.94a54.52,54.52,0,0,0,9.29-.78l77.41,20.93L256,229.42,83,142.17,256,58Z"/><path class="cls-2" d="M256,130.18c8.43,0,15.9,2.18,20.52,5.55l140,37.84v72.75a29.68,29.68,0,0,0-18.51,0V192l-132.36-35.8a45,45,0,0,1-9.6,1c-14,0-25.42-6.05-25.42-13.53S242,130.18,256,130.18Z"/><circle class="cls-3" cx="407.23" cy="274.28" r="22.68"/></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="56" height="56" viewBox="0 0 56 56" role="img" aria-label="Clubedge Starter"><title>Clubedge Starter</title><g fill="#2563EB"><rect x="0" y="0" width="16" height="16" rx="3"/><rect x="20" y="0" width="16" height="16" rx="3"/><rect x="0" y="20" width="16" height="16" rx="3"/><rect x="0" y="40" width="16" height="16" rx="3"/><rect x="20" y="40" width="16" height="16" rx="3"/><circle cx="48" cy="8" r="8"/><circle cx="48" cy="48" r="8"/></g></svg>
@@ -180,7 +180,7 @@ function Title({ className }: { className?: string }) {
180
180
  href="#overview"
181
181
  className={cn("inline-flex min-w-0 items-center gap-2.5 font-semibold", className)}
182
182
  >
183
- <ClubedgeMark alt="" className="size-6 shrink-0 rounded-md" />
183
+ <ClubedgeMark alt="" className="size-6 shrink-0" />
184
184
  <span className="truncate">{siteConfig.shortName}</span>
185
185
  </Link>
186
186
  );
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60" role="img" aria-label="Clubedge Starter"><title>Clubedge Starter</title><g fill="#2563EB"><rect x="2" y="2" width="16" height="16" rx="3"/><rect x="22" y="2" width="16" height="16" rx="3"/><rect x="2" y="22" width="16" height="16" rx="3"/><rect x="2" y="42" width="16" height="16" rx="3"/><rect x="22" y="42" width="16" height="16" rx="3"/><circle cx="50" cy="10" r="8"/><circle cx="50" cy="50" r="8"/></g></svg>
@@ -24,7 +24,7 @@ export function AuthPage({
24
24
  className="rounded-xl outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
25
25
  href="/"
26
26
  >
27
- <ClubedgeMark className="size-12 rounded-xl" />
27
+ <ClubedgeMark className="size-12" />
28
28
  </Link>
29
29
  <div className="grid gap-1.5">
30
30
  <h1 className="text-2xl font-semibold tracking-tight">{title}</h1>
@@ -57,7 +57,7 @@ export default async function LoginPage({ searchParams }: LoginPageProps) {
57
57
  className="rounded-xl outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
58
58
  href="/"
59
59
  >
60
- <ClubedgeMark className="size-12 rounded-xl" />
60
+ <ClubedgeMark className="size-12" />
61
61
  </Link>
62
62
  <div className="grid gap-1.5">
63
63
  <h1 className="text-2xl font-semibold tracking-tight">
@@ -60,7 +60,7 @@ export default function LandingPage() {
60
60
  className="flex shrink-0 items-center gap-2.5 rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring"
61
61
  href="/"
62
62
  >
63
- <ClubedgeMark alt="" className="size-9 rounded-xl" priority />
63
+ <ClubedgeMark alt="" className="size-9" priority />
64
64
  <span className="hidden text-sm font-semibold tracking-tight min-[380px]:inline">
65
65
  {siteConfig.name}
66
66
  </span>
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clubedge-starter-v1",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "packageManager": "pnpm@10.9.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/auth",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/auth-appwrite",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Appwrite provider for @clubedge/auth, run from the server with an API key and session cookies.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/auth-better-auth",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Better Auth adapter for @clubedge/auth, storing users and sessions in your own PostgreSQL.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/auth-firebase",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Firebase Authentication provider for @clubedge/auth, run from the server with session cookies.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/auth-supabase",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Supabase Auth adapter for @clubedge/auth.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/cache",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/cache-redis",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Redis rate limiter and cache for @clubedge/cache.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/contract",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/core",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/db",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/email",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Email interface, console and in-memory providers, and message templates.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/email-resend",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Resend provider for @clubedge/email, through Resend's HTTP API.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/email-smtp",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "SMTP provider for @clubedge/email, for any SMTP server.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/jobs",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Background jobs interface: job definitions, a queue contract, and an in-memory queue.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/jobs-bullmq",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "BullMQ provider for @clubedge/jobs: a job queue stored in Redis.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/jobs-inngest",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Inngest provider for @clubedge/jobs: jobs run by Inngest, which calls the app for each one.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/jobs-pg-boss",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "pg-boss provider for @clubedge/jobs: a job queue stored in the project's PostgreSQL.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/local-db",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "PostgreSQL for development without Docker: PGlite served over the PostgreSQL protocol.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/mobile",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "The mobile app's native helpers, through Capacitor, and its own screens: the update notice and the demo.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/observability",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Observability interface: error reports and spans for @clubedge/core's telemetry hooks, and an in-memory provider for tests.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/observability-otel",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "OpenTelemetry provider for @clubedge/observability: traces and error logs exported over OTLP to any collector.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/observability-sentry",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Sentry provider for @clubedge/observability: server and browser errors, traces, and source map uploads.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/payments",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Provider-neutral payments: the PaymentProvider interface, billing logic, and subscription tables.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/payments-stripe",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Stripe provider for @clubedge/payments: Checkout, the customer portal, and verified webhooks.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/storage",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/storage-s3",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "S3-compatible adapter for @clubedge/storage.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/storage-supabase",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "Supabase Storage adapter for @clubedge/storage.",
5
5
  "private": true,
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubedge/ui",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "repository": "Clubedge/clubedge-starter",
3
- "ref": "v0.27.0",
4
- "commit": "5f0e7e57131c4c56305d67edfd2e7366618d7b7c",
3
+ "ref": "v0.27.1",
4
+ "commit": "997d71d74775352d30dc2bb25017f46ef43f4fd8",
5
5
  "files": [
6
6
  ".dockerignore",
7
7
  ".env.example",
@@ -195,10 +195,12 @@
195
195
  "apps/api/src/tracing.ts",
196
196
  "apps/api/tsconfig.json",
197
197
  "apps/capacitor/.env.example",
198
+ "apps/capacitor/assets/logo.svg",
198
199
  "apps/capacitor/capacitor.config.ts",
199
200
  "apps/capacitor/clubedge.stack.json",
200
201
  "apps/capacitor/package.json",
201
202
  "apps/capacitor/scripts/build.mjs",
203
+ "apps/capacitor/scripts/icons.mjs",
202
204
  "apps/capacitor/tsconfig.json",
203
205
  "apps/start/.env.example",
204
206
  "apps/start/AGENTS.md",
@@ -208,7 +210,9 @@
208
210
  "apps/start/netlify.toml",
209
211
  "apps/start/package.json",
210
212
  "apps/start/public/.well-known/logo.svg",
213
+ "apps/start/public/apple-touch-icon.png",
211
214
  "apps/start/public/favicon.ico",
215
+ "apps/start/public/favicon.svg",
212
216
  "apps/start/scripts/check-env.ts",
213
217
  "apps/start/scripts/check-services.ts",
214
218
  "apps/start/src/components/auth/auth-form-controls.tsx",
@@ -320,7 +324,9 @@
320
324
  "apps/vite/index.html",
321
325
  "apps/vite/package.json",
322
326
  "apps/vite/public/.well-known/logo.svg",
327
+ "apps/vite/public/apple-touch-icon.png",
323
328
  "apps/vite/public/favicon.ico",
329
+ "apps/vite/public/favicon.svg",
324
330
  "apps/vite/server.mjs",
325
331
  "apps/vite/src/components/billing/billing-card.tsx",
326
332
  "apps/vite/src/components/branding/clubedge-mark.tsx",
@@ -382,6 +388,7 @@
382
388
  "apps/web/src/app/api/jobs/run/route.ts",
383
389
  "apps/web/src/app/api/ready/route.ts",
384
390
  "apps/web/src/app/api/webhooks/payments/route.ts",
391
+ "apps/web/src/app/apple-icon.png",
385
392
  "apps/web/src/app/auth/callback/route.ts",
386
393
  "apps/web/src/app/dashboard/_components/account-button.tsx",
387
394
  "apps/web/src/app/dashboard/_components/billing-actions.ts",
@@ -395,6 +402,7 @@
395
402
  "apps/web/src/app/error.tsx",
396
403
  "apps/web/src/app/favicon.ico",
397
404
  "apps/web/src/app/forgot-password/page.tsx",
405
+ "apps/web/src/app/icon.svg",
398
406
  "apps/web/src/app/layout.tsx",
399
407
  "apps/web/src/app/login/_components/auth-form-controls.tsx",
400
408
  "apps/web/src/app/login/_components/auth-page.tsx",