studiocms 0.1.0 → 0.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # studiocms
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1197](https://github.com/withstudiocms/studiocms/pull/1197) [`4b542ec`](https://github.com/withstudiocms/studiocms/commit/4b542eca8934996f7ed9eaf1c9f040305ea5e471) Thanks [@Adammatthiesen](https://github.com/Adammatthiesen)! - Tweaks optional dependencies, and chunkSizeWarningLimit for Astro config to prevent warnings from WYSIWYG plugin
8
+
9
+ - [#1219](https://github.com/withstudiocms/studiocms/pull/1219) [`9122ddd`](https://github.com/withstudiocms/studiocms/commit/9122ddd16f9c7ab61c5df227ae7a81edd8620bb0) Thanks [@Adammatthiesen](https://github.com/Adammatthiesen)! - Move to updated and migrated cli-kit package
10
+
11
+ ### Patch Changes
12
+
13
+ - [#1222](https://github.com/withstudiocms/studiocms/pull/1222) [`0f6b4c7`](https://github.com/withstudiocms/studiocms/commit/0f6b4c74886f09ebf35ee73d5d8579d26f8e534a) Thanks [@Adammatthiesen](https://github.com/Adammatthiesen)! - Tweaks package linking to pnpm catalog
14
+
15
+ - [#1211](https://github.com/withstudiocms/studiocms/pull/1211) [`b269e44`](https://github.com/withstudiocms/studiocms/commit/b269e44d68c8fd0da8eb3147c75b7d1cc899580d) Thanks [@Adammatthiesen](https://github.com/Adammatthiesen)! - Adds regex and proper error handling to prevent illegal characters (non-url-safe) from being used for S3 objects.
16
+
17
+ - [#1220](https://github.com/withstudiocms/studiocms/pull/1220) [`3324f2b`](https://github.com/withstudiocms/studiocms/commit/3324f2be74a6c7d21005d1cc0b4a8695f376c53b) Thanks [@kunjabijukchhe](https://github.com/kunjabijukchhe)! - Fixes an issue where saving a page that does not have `draft` set to true, would previously update the `publishedAt` date value.
18
+
19
+ - [#1214](https://github.com/withstudiocms/studiocms/pull/1214) [`efc10be`](https://github.com/withstudiocms/studiocms/commit/efc10bee20db090fdd75463622c30dda390c50ad) Thanks [@Adammatthiesen](https://github.com/Adammatthiesen)! - Fix: Reworks permission checks for dashboard routes to be at the middleware level to prevent unauthorized access
20
+
21
+ - Updated dependencies [[`0f6b4c7`](https://github.com/withstudiocms/studiocms/commit/0f6b4c74886f09ebf35ee73d5d8579d26f8e534a), [`93e62f6`](https://github.com/withstudiocms/studiocms/commit/93e62f65f779192403361826bc2a7fb997762521), [`2dd709f`](https://github.com/withstudiocms/studiocms/commit/2dd709f7f83efbb64c4ccb83f49db2d589ca9404), [`0f6b4c7`](https://github.com/withstudiocms/studiocms/commit/0f6b4c74886f09ebf35ee73d5d8579d26f8e534a), [`4b542ec`](https://github.com/withstudiocms/studiocms/commit/4b542eca8934996f7ed9eaf1c9f040305ea5e471), [`4b542ec`](https://github.com/withstudiocms/studiocms/commit/4b542eca8934996f7ed9eaf1c9f040305ea5e471), [`e628b43`](https://github.com/withstudiocms/studiocms/commit/e628b431f3128da1ad378138bdda2ca14794e76e), [`8a0ea71`](https://github.com/withstudiocms/studiocms/commit/8a0ea7176350b9526203d5722e1ff45d7fe6dfeb), [`59e5517`](https://github.com/withstudiocms/studiocms/commit/59e5517963cfd5f62fd3631b5ee69ae1e423ef50), [`c68668b`](https://github.com/withstudiocms/studiocms/commit/c68668b0a83341dd6cbdc378e1673017afef1d73)]:
22
+ - @withstudiocms/cli-kit@0.2.0
23
+ - @withstudiocms/effect@0.2.0
24
+ - @withstudiocms/kysely@0.2.0
25
+ - @withstudiocms/sdk@0.2.0
26
+ - @withstudiocms/auth-kit@0.1.2
27
+ - @withstudiocms/component-registry@0.1.2
28
+
29
+ ## 0.1.1
30
+
31
+ ### Patch Changes
32
+
33
+ - [#1181](https://github.com/withstudiocms/studiocms/pull/1181) [`a169a89`](https://github.com/withstudiocms/studiocms/commit/a169a893338947b425e87057cc77401f33abcbfd) Thanks [@renovate](https://github.com/apps/renovate)! - fix(deps): update dependency @iconify-json/simple-icons to ^1.2.66
34
+
35
+ - [#1186](https://github.com/withstudiocms/studiocms/pull/1186) [`415a512`](https://github.com/withstudiocms/studiocms/commit/415a51241ffddf5045ad8f8d695a5f40a86b5af7) Thanks [@Adammatthiesen](https://github.com/Adammatthiesen)! - fix workspace package dependency specifiers
36
+
37
+ - [#1187](https://github.com/withstudiocms/studiocms/pull/1187) [`1b2a0c5`](https://github.com/withstudiocms/studiocms/commit/1b2a0c57299544caeba18205ca85a8ca0381d7cb) Thanks [@aliozinan](https://github.com/aliozinan)! - Fix create folder bug in s3-storage PUT request handler
38
+
39
+ - Updated dependencies [[`415a512`](https://github.com/withstudiocms/studiocms/commit/415a51241ffddf5045ad8f8d695a5f40a86b5af7)]:
40
+ - @withstudiocms/component-registry@0.1.1
41
+ - @withstudiocms/auth-kit@0.1.1
42
+ - @withstudiocms/sdk@0.1.1
43
+
3
44
  ## 0.1.0
4
45
 
5
46
  ### Patch Changes
@@ -123,7 +123,7 @@ const addPlugin = Cli.Command.make(
123
123
  {
124
124
  plugin
125
125
  },
126
- ({ plugin: plugin2 }) => Effect.gen(function* () {
126
+ Effect.fn(function* ({ plugin: plugin2 }) {
127
127
  const [validator, installer, updater, context] = yield* Effect.all([
128
128
  ValidatePlugins,
129
129
  TryToInstallPlugins,
@@ -211,7 +211,7 @@ const addPlugin = Cli.Command.make(
211
211
  );
212
212
  }
213
213
  }
214
- }).pipe(Effect.provide(addPluginDeps))
214
+ }, Effect.provide(addPluginDeps))
215
215
  ).pipe(Cli.Command.withDescription("Add StudioCMS plugin(s) to your project"));
216
216
  export {
217
217
  ALIASES,
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs";
2
+ import { styleText } from "node:util";
2
3
  import {
3
4
  StudioCMSColorway,
4
5
  StudioCMSColorwayBg,
@@ -60,7 +61,7 @@ const genJWT = Cli.Command.make(
60
61
  const context = yield* genContext;
61
62
  const { chalk } = context;
62
63
  if (debug2) logger.debug("Init complete, starting...");
63
- yield* intro(label("StudioCMS Crypto: Generate JWT", StudioCMSColorwayBg, chalk.bold));
64
+ yield* intro(label("StudioCMS Crypto: Generate JWT", StudioCMSColorwayBg, "bold"));
64
65
  const spin = yield* spinner();
65
66
  try {
66
67
  yield* spin.start("Getting Key from keyFile");
@@ -89,14 +90,14 @@ const genJWT = Cli.Command.make(
89
90
  const base64UrlJwt = yield* convertJwtToBase64Url(jwt);
90
91
  yield* spin.stop("Token Generated.");
91
92
  yield* log.success(
92
- boxen(chalk.bold(`${label("Token Generated!", StudioCMSColorwayInfoBg, chalk.bold)}`), {
93
+ boxen(chalk.bold(`${label("Token Generated!", StudioCMSColorwayInfoBg, "bold")}`), {
93
94
  ln1: "Your new Token has been generated successfully:",
94
95
  ln3: `Token: ${chalk.magenta(jwt)}`,
95
96
  ln5: `Base64Url Token: ${chalk.blue(base64UrlJwt)}`
96
97
  })
97
98
  );
98
99
  yield* outro(
99
- `${label("You can now use this token where needed.", StudioCMSColorwayBg, chalk.bold)} Stuck? Join us on Discord at ${StudioCMSColorway.bold.underline("https://chat.studiocms.dev")}`
100
+ `${label("You can now use this token where needed.", StudioCMSColorwayBg, "bold")} Stuck? Join us on Discord at ${StudioCMSColorway(styleText(["bold", "underline"], "https://chat.studiocms.dev"))}`
100
101
  );
101
102
  } catch (err) {
102
103
  if (err instanceof Error) {
@@ -5,7 +5,6 @@ import { loadConfigFile } from "@withstudiocms/config-utils";
5
5
  import { Cli, Effect } from "@withstudiocms/effect";
6
6
  import { intro, log, outro } from "@withstudiocms/effect/clack";
7
7
  import { DebugInfoProvider } from "@withstudiocms/internal_helpers/debug-info-provider";
8
- import chalk from "chalk";
9
8
  import { debug } from "../utils/debugOpt.js";
10
9
  import { StudioCMSCliError } from "../utils/errors.js";
11
10
  const astroConfigPaths = [
@@ -42,7 +41,7 @@ const loadConfig = (key) => Effect.tryPromise({
42
41
  const debugCMD = Cli.Command.make(
43
42
  "debug",
44
43
  { debug },
45
- ({ debug: _debug }) => Effect.gen(function* () {
44
+ Effect.fn(function* ({ debug: _debug }) {
46
45
  let debug2;
47
46
  if (typeof _debug !== "boolean") {
48
47
  debug2 = yield* _debug;
@@ -88,7 +87,7 @@ const debugCMD = Cli.Command.make(
88
87
  yield* Effect.log("Debug info gathered successfully.");
89
88
  }
90
89
  console.log("");
91
- yield* intro(`${label("StudioCMS Debug Information", StudioCMSColorwayBg, chalk.black)}`);
90
+ yield* intro(`${label("StudioCMS Debug Information", StudioCMSColorwayBg, "black")}`);
92
91
  yield* log.info(debugInfo);
93
92
  yield* outro();
94
93
  if (debug2) {
@@ -10,7 +10,7 @@ const shell = Cli.Args.text({ name: "shell" }).pipe(
10
10
  const getTurso = Cli.Command.make(
11
11
  "get-turso",
12
12
  { shell },
13
- ({ shell: rawShell }) => Effect.gen(function* () {
13
+ Effect.fn(function* ({ shell: rawShell }) {
14
14
  let shell2;
15
15
  if (typeof rawShell !== "string" && rawShell !== void 0) {
16
16
  shell2 = yield* rawShell;
@@ -39,7 +39,7 @@ const initCMD = Cli.Command.make(
39
39
  debugLogger(`Options: ${JSON.stringify({ debug: debug2, dry }, null, 2)}`),
40
40
  debugLogger(`Context: ${JSON.stringify(context, null, 2)}`),
41
41
  intro(
42
- `${label("StudioCMS", StudioCMSColorwayBg, context.chalk.black)} Interactive CLI - initializing...`
42
+ `${label("StudioCMS", StudioCMSColorwayBg, "black")} Interactive CLI - initializing...`
43
43
  )
44
44
  ]);
45
45
  yield* SCMS_Intro(debug2).pipe(cliContext);
@@ -1,6 +1,7 @@
1
1
  import crypto from "node:crypto";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
+ import { styleText } from "node:util";
4
5
  import {
5
6
  StudioCMSColorwayError,
6
7
  StudioCMSColorwayInfo,
@@ -44,7 +45,7 @@ const env = Effect.fn(function* (context, debug, dryRun) {
44
45
  yield* debugLogger(`Environment file exists: ${envExists}`);
45
46
  if (envExists) {
46
47
  yield* log.warn(
47
- `${label("Warning", StudioCMSColorwayWarnBg, chalk.black)} An environment file already exists. Would you like to overwrite it?`
48
+ `${label("Warning", StudioCMSColorwayWarnBg, "black")} An environment file already exists. Would you like to overwrite it?`
48
49
  );
49
50
  const confirm2 = yield* askToContinue();
50
51
  if (!confirm2) {
@@ -428,7 +429,7 @@ const env = Effect.fn(function* (context, debug, dryRun) {
428
429
  }
429
430
  if (dryRun) {
430
431
  context.tasks.push({
431
- title: `${StudioCMSColorwayInfo.bold("--dry-run")} ${chalk.dim("Skipping environment file creation")}`,
432
+ title: `${StudioCMSColorwayInfo(styleText("bold", "--dry-run"))} ${styleText("dim", "Skipping environment file creation")}`,
432
433
  task: async (message) => {
433
434
  message("Creating environment file... (skipped)");
434
435
  }
@@ -1,3 +1,4 @@
1
+ import { styleText } from "node:util";
1
2
  import {
2
3
  StudioCMSColorway,
3
4
  StudioCMSColorwayBg,
@@ -33,7 +34,7 @@ const next = (debug) => genLogger("studiocms/cli/init/steps/next")(function* ()
33
34
  log.success(
34
35
  boxen(
35
36
  chalk.bold(
36
- `${label("Init Complete!", StudioCMSColorwayInfoBg, chalk.bold)} Get started with StudioCMS:`
37
+ `${label("Init Complete!", StudioCMSColorwayInfoBg, "bold")} Get started with StudioCMS:`
37
38
  ),
38
39
  {
39
40
  ln1: `Ensure your ${chalk.cyanBright(".env")} file is configured correctly.`,
@@ -45,7 +46,7 @@ const next = (debug) => genLogger("studiocms/cli/init/steps/next")(function* ()
45
46
  ]);
46
47
  yield* Effect.all([
47
48
  outro(
48
- `${label("Enjoy your new CMS!", StudioCMSColorwayBg, chalk.bold)} Stuck? Join us on Discord at ${StudioCMSColorway.bold.underline("https://chat.studiocms.dev")}`
49
+ `${label("Enjoy your new CMS!", StudioCMSColorwayBg, "bold")} Stuck? Join us on Discord at ${StudioCMSColorway(styleText(["bold", "underline"], "https://chat.studiocms.dev"))}`
49
50
  ),
50
51
  debugLogger("Next steps complete")
51
52
  ]);
@@ -1,4 +1,5 @@
1
1
  import { pathToFileURL } from "node:url";
2
+ import { styleText } from "node:util";
2
3
  import { StudioCMSColorwayBg, StudioCMSColorwayInfoBg } from "@withstudiocms/cli-kit/colors";
3
4
  import { label } from "@withstudiocms/cli-kit/messages";
4
5
  import { Cli, Effect, runEffect } from "@withstudiocms/effect";
@@ -102,9 +103,7 @@ const migratorCMD = Cli.Command.make(
102
103
  debugLogger("Starting interactive CLI..."),
103
104
  debugLogger(`Options: ${JSON.stringify({ debug: debug2, rollback: rollback2 }, null, 2)}`),
104
105
  debugLogger(`Context: ${JSON.stringify(context, null, 2)}`),
105
- intro(
106
- `${label("StudioCMS Migrator", StudioCMSColorwayBg, context.chalk.black)} - initializing...`
107
- )
106
+ intro(`${label("StudioCMS Migrator", StudioCMSColorwayBg, "black")} - initializing...`)
108
107
  ]);
109
108
  const isRollback = rollback2 && !latest2 && !status2;
110
109
  const isLatest = !rollback2 && latest2 && !status2;
@@ -146,11 +145,11 @@ const migratorCMD = Cli.Command.make(
146
145
  const migrationPercent = appliedMigrations / migrationTotal * 100;
147
146
  const migrationTotalColor = migrationPercent === 100 ? context.chalk.green : migrationPercent > 50 ? context.chalk.yellow : context.chalk.red;
148
147
  const labelParts = [
149
- label("Migration Status", StudioCMSColorwayInfoBg, context.chalk.black),
148
+ label("Migration Status", StudioCMSColorwayInfoBg, "black"),
150
149
  label(
151
150
  `(${context.chalk.green(appliedMigrations.toString())}/${migrationTotalColor(migrationTotal.toString())}) Applied`,
152
- context.chalk.bold,
153
- context.chalk.black
151
+ (text) => styleText("bold", text),
152
+ "black"
154
153
  )
155
154
  ];
156
155
  const responseMessage = `${labelParts.join(" ")}
@@ -51,7 +51,7 @@ const usersCMD = Cli.Command.make(
51
51
  yield* Effect.all([
52
52
  debugLogger(`Context: ${JSON.stringify(context, null, 2)}`),
53
53
  intro(
54
- `${label("StudioCMS", StudioCMSColorwayBg, context.chalk.black)} Interactive CLI - initializing...`
54
+ `${label("StudioCMS", StudioCMSColorwayBg, "black")} Interactive CLI - initializing...`
55
55
  ),
56
56
  checkRequiredEnvVarsEffect(["CMS_ENCRYPTION_KEY"])
57
57
  ]);
@@ -1,7 +1,7 @@
1
1
  import { Effect } from "@withstudiocms/effect";
2
2
  import { log } from "@withstudiocms/effect/clack";
3
3
  import chalk from "chalk";
4
- const validateInputOrRePrompt = (opts) => Effect.gen(function* () {
4
+ const validateInputOrRePrompt = Effect.fn("validateInputOrRePrompt")(function* (opts) {
5
5
  const { prompt, checkEffect } = opts;
6
6
  while (true) {
7
7
  const input = yield* prompt;
@@ -1,3 +1,4 @@
1
+ import { styleText } from "node:util";
1
2
  import { getAvatarUrl } from "@withstudiocms/auth-kit/utils/libravatar";
2
3
  import { StudioCMSColorwayInfo } from "@withstudiocms/cli-kit/colors";
3
4
  import { group, log, password, select, text } from "@withstudiocms/effect/clack";
@@ -150,14 +151,14 @@ const createUsers = Effect.fn(function* (context, _debug, dryRun) {
150
151
  const todo = Effect.all([_createUser(newUser), _createRank(newRank)]);
151
152
  if (dryRun) {
152
153
  context.tasks.push({
153
- title: `${StudioCMSColorwayInfo.bold("--dry-run")} ${context.chalk.dim("Skipping user creation")}`,
154
+ title: `${StudioCMSColorwayInfo(styleText("bold", "--dry-run"))} ${styleText("dim", "Skipping user creation")}`,
154
155
  task: async (message) => {
155
156
  message("Creating user... (skipped)");
156
157
  }
157
158
  });
158
159
  } else {
159
160
  context.tasks.push({
160
- title: context.chalk.dim("Creating user..."),
161
+ title: styleText("dim", "Creating user..."),
161
162
  task: async (message) => {
162
163
  try {
163
164
  const [insertedUser, insertedRank] = await runEffect(todo);
@@ -1,3 +1,4 @@
1
+ import { styleText } from "node:util";
1
2
  import { StudioCMSColorwayError, StudioCMSColorwayInfo } from "@withstudiocms/cli-kit/colors";
2
3
  import { runEffect } from "@withstudiocms/effect";
3
4
  import { log, note, password, select, text } from "@withstudiocms/effect/clack";
@@ -125,14 +126,14 @@ const modifyUsers = Effect.fn(function* (context, _debug, dryRun) {
125
126
  }
126
127
  if (dryRun) {
127
128
  context.tasks.push({
128
- title: `${StudioCMSColorwayInfo.bold("--dry-run")} ${context.chalk.dim("Skipping user modification")}`,
129
+ title: `${StudioCMSColorwayInfo(styleText("bold", "--dry-run"))} ${styleText("dim", "Skipping user modification")}`,
129
130
  task: async (message) => {
130
131
  message("Modifying user... (skipped)");
131
132
  }
132
133
  });
133
134
  } else {
134
135
  context.tasks.push({
135
- title: context.chalk.dim("Modifying user..."),
136
+ title: styleText("dim", "Modifying user..."),
136
137
  task: async (message) => {
137
138
  try {
138
139
  await runEffect(
@@ -166,14 +167,14 @@ const modifyUsers = Effect.fn(function* (context, _debug, dryRun) {
166
167
  }
167
168
  if (dryRun) {
168
169
  context.tasks.push({
169
- title: `${StudioCMSColorwayInfo.bold("--dry-run")} ${context.chalk.dim("Skipping user modification")}`,
170
+ title: `${StudioCMSColorwayInfo(styleText("bold", "--dry-run"))} ${styleText("dim", "Skipping user modification")}`,
170
171
  task: async (message) => {
171
172
  message("Modifying user... (skipped)");
172
173
  }
173
174
  });
174
175
  } else {
175
176
  context.tasks.push({
176
- title: context.chalk.dim("Modifying user..."),
177
+ title: styleText("dim", "Modifying user..."),
177
178
  task: async (message) => {
178
179
  try {
179
180
  await runEffect(
@@ -207,14 +208,14 @@ const modifyUsers = Effect.fn(function* (context, _debug, dryRun) {
207
208
  }
208
209
  if (dryRun) {
209
210
  context.tasks.push({
210
- title: `${StudioCMSColorwayInfo.bold("--dry-run")} ${context.chalk.dim("Skipping user modification")}`,
211
+ title: `${StudioCMSColorwayInfo(styleText("bold", "--dry-run"))} ${styleText("dim", "Skipping user modification")}`,
211
212
  task: async (message) => {
212
213
  message("Modifying user... (skipped)");
213
214
  }
214
215
  });
215
216
  } else {
216
217
  context.tasks.push({
217
- title: context.chalk.dim("Modifying user..."),
218
+ title: styleText("dim", "Modifying user..."),
218
219
  task: async (message) => {
219
220
  try {
220
221
  const hashedPassword = await runEffect(hashPassword(newPassword));
@@ -1,3 +1,2 @@
1
1
  import { Effect } from '../../../effect.js';
2
- import { CliContext } from '../../utils/context.js';
3
- export declare const next: (debug: boolean) => Effect.Effect<void, import("effect/Cause").UnknownException | import("effect/ConfigError").ConfigError | import("@withstudiocms/effect/clack").ClackError, CliContext>;
2
+ export declare const next: (debug: boolean) => Effect.Effect<void, import("effect/Cause").UnknownException | import("effect/ConfigError").ConfigError | import("@withstudiocms/effect/clack").ClackError, never>;
@@ -1,14 +1,14 @@
1
+ import { styleText } from "node:util";
1
2
  import { StudioCMSColorway, StudioCMSColorwayBg } from "@withstudiocms/cli-kit/colors";
2
3
  import { label } from "@withstudiocms/cli-kit/messages";
3
4
  import { outro } from "@withstudiocms/effect/clack";
4
5
  import { Effect, genLogger } from "../../../effect.js";
5
- import { CliContext } from "../../utils/context.js";
6
6
  import { buildDebugLogger } from "../../utils/logger.js";
7
7
  const next = (debug) => genLogger("studiocms/cli/users/steps/next")(function* () {
8
- const [{ chalk }, debugLogger] = yield* Effect.all([CliContext, buildDebugLogger(debug)]);
8
+ const debugLogger = yield* buildDebugLogger(debug);
9
9
  yield* Effect.all([
10
10
  outro(
11
- `${label("Action Complete!", StudioCMSColorwayBg, chalk.bold)} Stuck? Join us on Discord at ${StudioCMSColorway.bold.underline("https://chat.studiocms.dev")}`
11
+ `${label("Action Complete!", StudioCMSColorwayBg, "bold")} Stuck? Join us on Discord at ${StudioCMSColorway(styleText(["bold", "underline"], "https://chat.studiocms.dev"))}`
12
12
  ),
13
13
  debugLogger("Next steps complete")
14
14
  ]);
@@ -11,7 +11,7 @@ const random = (...arr) => {
11
11
  return flattenedArray[Math.floor(flattenedArray.length * Math.random())];
12
12
  };
13
13
  const intro = (debug) => genLogger("studiocms/cli/utils/intro")(function* () {
14
- const { chalk, username } = yield* CliContext;
14
+ const { username } = yield* CliContext;
15
15
  const { messages } = getSeasonalMessages();
16
16
  const welcome = random(messages);
17
17
  debug && logger.debug("Printing welcome message...");
@@ -21,7 +21,7 @@ const intro = (debug) => genLogger("studiocms/cli/utils/intro")(function* () {
21
21
  [
22
22
  "Welcome",
23
23
  "to",
24
- label("StudioCMS", StudioCMSColorwayBg, chalk.black),
24
+ label("StudioCMS", StudioCMSColorwayBg, "black"),
25
25
  StudioCMSColorway(`v${pkgJson.version}`),
26
26
  username
27
27
  ],
package/dist/consts.js CHANGED
@@ -93,7 +93,8 @@ const AstroConfigImageSettings = {
93
93
  };
94
94
  const AstroConfigViteSettings = {
95
95
  build: {
96
- chunkSizeWarningLimit: 700,
96
+ chunkSizeWarningLimit: 1200,
97
+ // Allow's increase to 1.2 kB for Plugins such as our WYSIWYG editor to not trigger warnings
97
98
  rollupOptions: {
98
99
  // Users will need to install these peer dependencies themselves
99
100
  external: ["@libsql/client", "kysely-turso", "pg", "mysql2"]
@@ -34,10 +34,10 @@ export declare const Encryption: Effect.Effect<{
34
34
  readonly createSession: (token: string, userId: string) => Effect.Effect<import("@withstudiocms/auth-kit/types").UserSession, import("@withstudiocms/auth-kit/errors").SessionError, never>;
35
35
  readonly validateSessionToken: (token: string) => Effect.Effect<import("@withstudiocms/auth-kit/types").SessionValidationResult, import("@withstudiocms/auth-kit/errors").SessionError, never>;
36
36
  readonly invalidateSession: (sessionId: string) => Effect.Effect<void, import("@withstudiocms/auth-kit/errors").SessionError, never>;
37
- readonly setSessionTokenCookie: (context: import("astro").APIContext<Record<string, any>, Record<string, string | undefined>> | import("astro").AstroGlobal<Record<string, any>, import("astro/runtime/server/index.js").AstroComponentFactory, Record<string, string | undefined>>, token: string, expiresAt: Date, secure?: boolean | undefined) => Effect.Effect<void, import("@withstudiocms/auth-kit/errors").SessionError, never>;
38
- readonly deleteSessionTokenCookie: (context: import("astro").APIContext<Record<string, any>, Record<string, string | undefined>> | import("astro").AstroGlobal<Record<string, any>, import("astro/runtime/server/index.js").AstroComponentFactory, Record<string, string | undefined>>, secure?: boolean | undefined) => Effect.Effect<void, import("@withstudiocms/auth-kit/errors").SessionError, never>;
39
- readonly setOAuthSessionTokenCookie: (context: import("astro").APIContext<Record<string, any>, Record<string, string | undefined>> | import("astro").AstroGlobal<Record<string, any>, import("astro/runtime/server/index.js").AstroComponentFactory, Record<string, string | undefined>>, key: string, value: string, secure?: boolean | undefined) => Effect.Effect<void, import("@withstudiocms/auth-kit/errors").SessionError, never>;
40
- readonly createUserSession: (userId: string, context: import("astro").APIContext<Record<string, any>, Record<string, string | undefined>> | import("astro").AstroGlobal<Record<string, any>, import("astro/runtime/server/index.js").AstroComponentFactory, Record<string, string | undefined>>, secure?: boolean | undefined) => Effect.Effect<void, import("@withstudiocms/auth-kit/errors").SessionError, never>;
37
+ readonly setSessionTokenCookie: (context: import("astro").APIContext | import("astro").AstroGlobal<Record<string, any>, import("astro/runtime/server/index.js").AstroComponentFactory, Record<string, string | undefined>>, token: string, expiresAt: Date, secure?: boolean | undefined) => Effect.Effect<void, import("@withstudiocms/auth-kit/errors").SessionError, never>;
38
+ readonly deleteSessionTokenCookie: (context: import("astro").APIContext | import("astro").AstroGlobal<Record<string, any>, import("astro/runtime/server/index.js").AstroComponentFactory, Record<string, string | undefined>>, secure?: boolean | undefined) => Effect.Effect<void, import("@withstudiocms/auth-kit/errors").SessionError, never>;
39
+ readonly setOAuthSessionTokenCookie: (context: import("astro").APIContext | import("astro").AstroGlobal<Record<string, any>, import("astro/runtime/server/index.js").AstroComponentFactory, Record<string, string | undefined>>, key: string, value: string, secure?: boolean | undefined) => Effect.Effect<void, import("@withstudiocms/auth-kit/errors").SessionError, never>;
40
+ readonly createUserSession: (userId: string, context: import("astro").APIContext | import("astro").AstroGlobal<Record<string, any>, import("astro/runtime/server/index.js").AstroComponentFactory, Record<string, string | undefined>>, secure?: boolean | undefined) => Effect.Effect<void, import("@withstudiocms/auth-kit/errors").SessionError, never>;
41
41
  }, import("@withstudiocms/auth-kit/errors").SessionError, never>, User: Effect.Effect<{
42
42
  readonly verifyUsernameInput: (username: string) => Effect.Effect<string | true, import("@withstudiocms/auth-kit/errors").CheckIfUnsafeError | import("@withstudiocms/auth-kit/errors").UserError, never>;
43
43
  readonly createUserAvatar: (email: string) => Effect.Effect<string, import("@withstudiocms/auth-kit/errors").UserError, never>;
@@ -49,7 +49,7 @@ export declare const Encryption: Effect.Effect<{
49
49
  readonly updateUserPassword: (userId: string, password: string) => Effect.Effect<import("@withstudiocms/auth-kit/types").UserData, import("@withstudiocms/effect/scrypt").ScryptError | import("@withstudiocms/auth-kit/errors").UserError, never>;
50
50
  readonly getUserPasswordHash: (userId: string) => Effect.Effect<string, import("@withstudiocms/auth-kit/errors").UserError, never>;
51
51
  readonly getUserFromEmail: (email: string) => Effect.Effect<import("@withstudiocms/auth-kit/types").CombinedUserData | null | undefined, import("@withstudiocms/auth-kit/errors").UserError, never>;
52
- readonly getUserData: (context: import("astro").APIContext<Record<string, any>, Record<string, string | undefined>> | import("astro").AstroGlobal<Record<string, any>, import("astro/runtime/server/index.js").AstroComponentFactory, Record<string, string | undefined>>) => Effect.Effect<import("@withstudiocms/auth-kit/types").UserSessionData, import("@withstudiocms/auth-kit/errors").SessionError | import("@withstudiocms/auth-kit/errors").UserError, never>;
52
+ readonly getUserData: (context: import("astro").APIContext | import("astro").AstroGlobal<Record<string, any>, import("astro/runtime/server/index.js").AstroComponentFactory, Record<string, string | undefined>>) => Effect.Effect<import("@withstudiocms/auth-kit/types").UserSessionData, import("@withstudiocms/auth-kit/errors").SessionError | import("@withstudiocms/auth-kit/errors").UserError, never>;
53
53
  readonly getUserPermissionLevel: (userData: import("@withstudiocms/auth-kit/types").UserSessionData | import("@withstudiocms/auth-kit/types").CombinedUserData | null) => Effect.Effect<import("@withstudiocms/auth-kit/types").UserPermissionLevel, import("@withstudiocms/auth-kit/errors").UserError, never>;
54
54
  readonly isUserAllowed: (userData: import("@withstudiocms/auth-kit/types").UserSessionData | import("@withstudiocms/auth-kit/types").CombinedUserData | null, requiredPerms: "owner" | "admin" | "editor" | "visitor" | "unknown") => Effect.Effect<boolean, import("@withstudiocms/auth-kit/errors").UserError, never>;
55
55
  }, import("@withstudiocms/auth-kit/errors").SessionError | import("@withstudiocms/auth-kit/errors").UserError, never>;
@@ -7,6 +7,7 @@ interface MimeTypeMap {
7
7
  [key: string]: string;
8
8
  }
9
9
  type StorageReturnType = 'url' | 'identifier' | 'key';
10
+ declare const s3SafeNameRegex: RegExp;
10
11
  /**
11
12
  * Translation strings for the StorageFileBrowser
12
13
  */
@@ -77,6 +78,11 @@ interface TranslationStrings {
77
78
  andAllItsContents: string;
78
79
  filePreview: string;
79
80
  }
81
+ declare class InvalidFileNameError extends Error {
82
+ #private;
83
+ constructor(files: string[]);
84
+ get files(): string[];
85
+ }
80
86
  /**
81
87
  * StorageFileBrowser Custom Element
82
88
  * A web component for browsing and selecting files from cloud storage
@@ -155,6 +161,7 @@ declare class StorageFileBrowser extends HTMLElement {
155
161
  private deleteFolder;
156
162
  private showCreateFolderDialog;
157
163
  private createFolder;
164
+ private escapeHtml;
158
165
  private uploadFilesWithCustomNames;
159
166
  private uploadSingleFile;
160
167
  private showRenameFolderDialog;
@@ -1,3 +1,17 @@
1
+ const s3SafeNameRegex = /^[a-zA-Z0-9._-]+(?:\/[a-zA-Z0-9._-]+)*$/;
2
+ class InvalidFileNameError extends Error {
3
+ #files;
4
+ constructor(files) {
5
+ super(
6
+ "The following filenames are invalid: (Only alphanumeric characters and . _ - / are allowed.)"
7
+ );
8
+ this.#files = files;
9
+ this.name = "InvalidFileNameError";
10
+ }
11
+ get files() {
12
+ return this.#files;
13
+ }
14
+ }
1
15
  class StorageFileBrowser extends HTMLElement {
2
16
  currentPath = "";
3
17
  selectedFile = null;
@@ -1208,7 +1222,7 @@ class StorageFileBrowser extends HTMLElement {
1208
1222
  method: "PUT",
1209
1223
  headers: {
1210
1224
  "Content-Type": "text/plain",
1211
- "x-s3-key": folderKey
1225
+ "x-storage-key": folderKey
1212
1226
  },
1213
1227
  body: ""
1214
1228
  });
@@ -1230,8 +1244,27 @@ class StorageFileBrowser extends HTMLElement {
1230
1244
  setTimeout(() => this.loadFiles(), 2e3);
1231
1245
  }
1232
1246
  }
1247
+ escapeHtml(value) {
1248
+ return value.replace(/[&<>"']/g, (char) => {
1249
+ switch (char) {
1250
+ case "&":
1251
+ return "&amp;";
1252
+ case "<":
1253
+ return "&lt;";
1254
+ case ">":
1255
+ return "&gt;";
1256
+ case '"':
1257
+ return "&quot;";
1258
+ case "'":
1259
+ return "&#39;";
1260
+ default:
1261
+ return char;
1262
+ }
1263
+ });
1264
+ }
1233
1265
  async uploadFilesWithCustomNames(customNames) {
1234
1266
  if (this.isUploading) return;
1267
+ const status = { valid: true, invalidFiles: [] };
1235
1268
  this.isUploading = true;
1236
1269
  const content = this.$(`#${this.contentId}`);
1237
1270
  if (!content) return;
@@ -1254,6 +1287,16 @@ class StorageFileBrowser extends HTMLElement {
1254
1287
  ".storage-browser-progress-text"
1255
1288
  );
1256
1289
  try {
1290
+ this.pendingFiles.forEach((file, index) => {
1291
+ const customName = customNames[index];
1292
+ if (customName && !s3SafeNameRegex.test(customName)) {
1293
+ status.valid = false;
1294
+ status.invalidFiles.push(file.name);
1295
+ }
1296
+ });
1297
+ if (!status.valid) {
1298
+ throw new InvalidFileNameError(status.invalidFiles);
1299
+ }
1257
1300
  for (let i = 0; i < this.pendingFiles.length; i++) {
1258
1301
  const file = this.pendingFiles[i];
1259
1302
  const customName = customNames[i] || `${Date.now()}-${file.name}`;
@@ -1271,13 +1314,22 @@ class StorageFileBrowser extends HTMLElement {
1271
1314
  await this.loadFiles();
1272
1315
  } catch (error) {
1273
1316
  console.error("Upload error:", error);
1317
+ let errorMessage = this.t("failedToUploadFiles");
1318
+ let extraInfo;
1319
+ if (error instanceof Error) {
1320
+ errorMessage = error.message;
1321
+ if (error instanceof InvalidFileNameError) {
1322
+ extraInfo = this.escapeHtml(error.files.join(", "));
1323
+ }
1324
+ }
1274
1325
  content.innerHTML = `
1275
1326
  <div class="storage-browser-error" role="alert" aria-live="assertive">
1276
1327
  <svg width="48" height="48" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
1277
1328
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
1278
1329
  </svg>
1279
1330
  <p>${this.t("failedToUploadFiles")}</p>
1280
- <p style="font-size: 0.875rem; opacity: 0.7;">${error instanceof Error ? error.message : this.t("unknownError")}</p>
1331
+ <p style="font-size: 0.875rem; opacity: 0.7;">${errorMessage}</p>
1332
+ ${extraInfo ? `<p style="font-size: 0.75rem; opacity: 0.5;">${extraInfo}</p>` : ""}
1281
1333
  </div>
1282
1334
  `;
1283
1335
  } finally {
@@ -1410,6 +1462,9 @@ class StorageFileBrowser extends HTMLElement {
1410
1462
  </div>
1411
1463
  `;
1412
1464
  try {
1465
+ if (!s3SafeNameRegex.test(newFileName)) {
1466
+ throw new InvalidFileNameError([newFileName]);
1467
+ }
1413
1468
  const pathParts = this.fileToRename.key.split("/");
1414
1469
  const _oldFileName = pathParts.pop() || "";
1415
1470
  const path = pathParts.length > 0 ? `${pathParts.join("/")}/` : "";
@@ -1437,13 +1492,22 @@ class StorageFileBrowser extends HTMLElement {
1437
1492
  await this.loadFiles();
1438
1493
  } catch (error) {
1439
1494
  console.error("Rename error:", error);
1495
+ let errorMessage = this.t("unknownError");
1496
+ let extraInfo;
1497
+ if (error instanceof Error) {
1498
+ errorMessage = error.message;
1499
+ if (error instanceof InvalidFileNameError) {
1500
+ extraInfo = this.escapeHtml(error.files.join(", "));
1501
+ }
1502
+ }
1440
1503
  content.innerHTML = `
1441
1504
  <div class="storage-browser-error" role="alert">
1442
1505
  <svg width="48" height="48" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1443
1506
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
1444
1507
  </svg>
1445
1508
  <p>${this.t("failedToRenameFile")}</p>
1446
- <p style="font-size: 0.875rem; opacity: 0.7;">${error instanceof Error ? error.message : this.t("unknownError")}</p>
1509
+ <p style="font-size: 0.875rem; opacity: 0.7;">${errorMessage}</p>
1510
+ ${extraInfo ? `<p style="font-size: 0.75rem; opacity: 0.5;">${extraInfo}</p>` : ""}
1447
1511
  </div>
1448
1512
  `;
1449
1513
  setTimeout(() => this.loadFiles(), 2e3);
@@ -0,0 +1,63 @@
1
+ import { dashboardConfig } from 'studiocms:config';
2
+
3
+ type AuthenticatedRoute = {
4
+ pathname: string;
5
+ requiredPermissionLevel: 'owner' | 'admin' | 'editor' | 'visitor';
6
+ };
7
+
8
+ // Import the dashboard route override from the configuration
9
+ // If no override is set, it defaults to 'dashboard'
10
+ // This allows for flexibility in the dashboard route without hardcoding it
11
+ const dashboardRoute = dashboardConfig.dashboardRouteOverride || 'dashboard';
12
+
13
+ /**
14
+ * List of authenticated routes with their required permission levels.
15
+ * This list is used to determine if a user has the necessary permissions
16
+ * to access specific dashboard routes.
17
+ */
18
+ export const authenticatedRoutes: AuthenticatedRoute[] = [
19
+ {
20
+ pathname: `/${dashboardRoute}/system-management`,
21
+ requiredPermissionLevel: 'owner',
22
+ },
23
+ {
24
+ pathname: `/${dashboardRoute}/smtp-configuration`,
25
+ requiredPermissionLevel: 'owner',
26
+ },
27
+ {
28
+ pathname: `/${dashboardRoute}`,
29
+ requiredPermissionLevel: 'editor',
30
+ },
31
+ {
32
+ pathname: `/${dashboardRoute}/user-management`,
33
+ requiredPermissionLevel: 'admin',
34
+ },
35
+ {
36
+ pathname: `/${dashboardRoute}/user-management/**`,
37
+ requiredPermissionLevel: 'admin',
38
+ },
39
+ {
40
+ pathname: `/${dashboardRoute}/taxonomy`,
41
+ requiredPermissionLevel: 'editor',
42
+ },
43
+ {
44
+ pathname: `/${dashboardRoute}/taxonomy/categories`,
45
+ requiredPermissionLevel: 'editor',
46
+ },
47
+ {
48
+ pathname: `/${dashboardRoute}/taxonomy/tags`,
49
+ requiredPermissionLevel: 'editor',
50
+ },
51
+ {
52
+ pathname: `/${dashboardRoute}/plugins/**`,
53
+ requiredPermissionLevel: 'editor',
54
+ },
55
+ {
56
+ pathname: `/${dashboardRoute}/content-management`,
57
+ requiredPermissionLevel: 'editor',
58
+ },
59
+ {
60
+ pathname: `/${dashboardRoute}/content-management/**`,
61
+ requiredPermissionLevel: 'editor',
62
+ },
63
+ ];
@@ -19,7 +19,9 @@ import { SDKCore } from 'studiocms:sdk';
19
19
  import SCMSUiVersion from 'studiocms:ui/version';
20
20
  import SCMSVersion from 'studiocms:version';
21
21
  import { defineMiddlewareRouter, Effect } from '@withstudiocms/effect';
22
+ import micromatch from 'micromatch';
22
23
  import { STUDIOCMS_EDITOR_CSRF_COOKIE_NAME } from '#consts';
24
+ import { authenticatedRoutes } from './_authmap.js';
23
25
  import { getUserPermissions, makeFallbackSiteConfig, SetLocal, setLocals } from './utils.js';
24
26
 
25
27
  // Import the dashboard route override from the configuration
@@ -145,6 +147,50 @@ export const onRequest = defineMiddlewareRouter([
145
147
  // Check if the user is logged in and redirect to the login page if not
146
148
  if (!userSessionData.isLoggedIn) return context.redirect(StudioCMSRoutes.authLinks.loginURL);
147
149
 
150
+ // Get the current path
151
+ const currentPath = context.url.pathname;
152
+
153
+ // Check if the user has permission to access the current route
154
+ // If not, redirect to the dashboard home page
155
+ const userPermissionLevel =
156
+ context.locals.StudioCMS.security?.userSessionData.permissionLevel;
157
+
158
+ if (!userPermissionLevel) {
159
+ // How did the user get here? Log them out to reset session
160
+ return context.redirect(`/${dashboardRoute}/logout`);
161
+ }
162
+
163
+ // Using micromatch to handle wildcard route matching
164
+ const matchChance1 = authenticatedRoutes.find((route) =>
165
+ micromatch.isMatch(currentPath, route.pathname)
166
+ );
167
+
168
+ // if trailing `/` exists, try matching without it
169
+ const matchChance2 =
170
+ matchChance1 ||
171
+ (() => {
172
+ if (currentPath.endsWith('/')) {
173
+ const trimmedPath = currentPath.slice(0, -1);
174
+ return authenticatedRoutes.find((route) =>
175
+ micromatch.isMatch(trimmedPath, route.pathname)
176
+ );
177
+ }
178
+ return null;
179
+ })();
180
+
181
+ if (matchChance1 || matchChance2) {
182
+ // biome-ignore lint/style/noNonNullAssertion: only used after checking for existence
183
+ const matchingRoute = matchChance1 || matchChance2!;
184
+ const requiredLevel = matchingRoute.requiredPermissionLevel;
185
+ const levels = ['visitor', 'editor', 'admin', 'owner'];
186
+ const userLevelIndex = levels.indexOf(userPermissionLevel);
187
+ const requiredLevelIndex = levels.indexOf(requiredLevel);
188
+
189
+ if (userLevelIndex < requiredLevelIndex) {
190
+ return context.redirect(`/${dashboardRoute}`);
191
+ }
192
+ }
193
+
148
194
  // Else, Continue to the next middleware
149
195
  return next();
150
196
  }),
@@ -225,7 +225,10 @@ export const { POST, PATCH, DELETE, OPTIONS, ALL } = createEffectAPIRoutes(
225
225
  authorId: AuthorId,
226
226
  contributorIds: JSON.stringify(ContributorIds),
227
227
  updatedAt: new Date().toISOString(),
228
- publishedAt: data.publishedAt?.toISOString() || new Date().toISOString(),
228
+ publishedAt:
229
+ currentPageData.draft && data.draft === false
230
+ ? new Date().toISOString()
231
+ : currentPageData.publishedAt?.toISOString() || new Date().toISOString(),
229
232
  categories: JSON.stringify(data.categories || []),
230
233
  tags: JSON.stringify(data.tags || []),
231
234
  augments: JSON.stringify(data.augments || []),
@@ -23,24 +23,23 @@ const useDriverErrorPromise = <T>(_try: () => Promise<T>) =>
23
23
  new DriverError({ message: error instanceof Error ? error.message : String(error) }),
24
24
  });
25
25
 
26
- const getDriverInstance = () =>
27
- Effect.gen(function* () {
28
- // Return existing driver if already initialized
29
- if (driver) return driver;
30
-
31
- // Attempt to get and initialize the driver
32
- driver = yield* useDriverErrorPromise(() => getDriver()).pipe(
33
- Effect.tap((drv) => useDriverErrorPromise(() => drv.init()))
34
- );
35
-
36
- // If driver is still undefined, return an error
37
- if (!driver) {
38
- return yield* new DriverError({ message: 'Failed to get database driver' });
39
- }
26
+ const getDriverInstance = Effect.fn('getDriverInstance')(function* () {
27
+ // Return existing driver if already initialized
28
+ if (driver) return driver;
29
+
30
+ // Attempt to get and initialize the driver
31
+ driver = yield* useDriverErrorPromise(() => getDriver()).pipe(
32
+ Effect.tap((drv) => useDriverErrorPromise(() => drv.init()))
33
+ );
34
+
35
+ // If driver is still undefined, return an error
36
+ if (!driver) {
37
+ return yield* new DriverError({ message: 'Failed to get database driver' });
38
+ }
40
39
 
41
- // Return the initialized driver
42
- return driver;
43
- });
40
+ // Return the initialized driver
41
+ return driver;
42
+ });
44
43
 
45
44
  const parseLogLevel = (
46
45
  level: 'All' | 'Fatal' | 'Error' | 'Warning' | 'Info' | 'Debug' | 'Trace' | 'None'
@@ -76,8 +76,8 @@ export const categoriesRouter: EndpointRoute = {
76
76
  ctx,
77
77
  StudioCMSPageDataCategories.Insert.omit('id')
78
78
  ).pipe(
79
- Effect.flatMap((data) =>
80
- Effect.gen(function* () {
79
+ Effect.flatMap(
80
+ Effect.fn(function* (data) {
81
81
  const id = yield* sdk.UTIL.Generators.generateRandomIDNumber(9);
82
82
  return { id, ...data };
83
83
  })
@@ -230,7 +230,10 @@ const pageIdRouter = (id: string) =>
230
230
  authorId: AuthorId,
231
231
  contributorIds: JSON.stringify(ContributorIds),
232
232
  updatedAt: new Date().toISOString(),
233
- publishedAt: data.publishedAt?.toISOString() || new Date().toISOString(),
233
+ publishedAt:
234
+ currentPageData.draft && data.draft === false
235
+ ? new Date().toISOString()
236
+ : currentPageData.publishedAt?.toISOString() || new Date().toISOString(),
234
237
  categories: JSON.stringify(data.categories || []),
235
238
  tags: JSON.stringify(data.tags || []),
236
239
  augments: JSON.stringify(data.augments || []),
@@ -65,8 +65,8 @@ export const tagsRouter: EndpointRoute = {
65
65
  }
66
66
 
67
67
  return yield* parseAPIContextJson(ctx, StudioCMSPageDataTags.Insert.omit('id')).pipe(
68
- Effect.flatMap((data) =>
69
- Effect.gen(function* () {
68
+ Effect.flatMap(
69
+ Effect.fn(function* (data) {
70
70
  const id = yield* sdk.UTIL.Generators.generateRandomIDNumber(9);
71
71
  return { id, ...data };
72
72
  })
@@ -218,34 +218,33 @@ export function idOrPathRouter(
218
218
  * const responseEffect = processHandler(myHandler, apiContext, '/my-path', 'GET');
219
219
  * ```
220
220
  */
221
- const processHandler = (
221
+ const processHandler = Effect.fn('processHandler')(function* (
222
222
  handler: APIRoute | undefined,
223
223
  ctx: APIContext,
224
224
  path: string,
225
225
  method: string
226
- ): Effect.Effect<Response, never, never> =>
227
- Effect.gen(function* () {
228
- if (!handler) {
229
- return AllResponse();
230
- }
226
+ ): Effect.fn.Return<Response, never, never> {
227
+ if (!handler) {
228
+ return AllResponse();
229
+ }
231
230
 
232
- const response = yield* Effect.tryPromise({
233
- try: async () => await handler(ctx),
234
- catch: (error) =>
235
- new StudioCMSAPIError({
236
- message: `Error in handler for path ${path} [${method}]: ${String(error)}`,
237
- cause: error,
238
- }),
239
- }).pipe(
240
- Effect.catchAll((error) =>
241
- Effect.logError(`API Route Error: ${String(error)}`).pipe(
242
- Effect.as(createJsonResponse({ error: 'Internal Server Error' }, { status: 500 }))
243
- )
231
+ const response = yield* Effect.tryPromise({
232
+ try: async () => await handler(ctx),
233
+ catch: (error) =>
234
+ new StudioCMSAPIError({
235
+ message: `Error in handler for path ${path} [${method}]: ${String(error)}`,
236
+ cause: error,
237
+ }),
238
+ }).pipe(
239
+ Effect.catchAll((error) =>
240
+ Effect.logError(`API Route Error: ${String(error)}`).pipe(
241
+ Effect.as(createJsonResponse({ error: 'Internal Server Error' }, { status: 500 }))
244
242
  )
245
- );
243
+ )
244
+ );
246
245
 
247
- return response;
248
- });
246
+ return response;
247
+ });
249
248
 
250
249
  /**
251
250
  * Creates a REST router that handles HTTP requests based on route type and optional ID parameters.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "studiocms",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "A Community-Driven Astro native CMS. Built from the ground up by the Astro community.",
5
5
  "author": {
6
6
  "name": "withstudiocms",
@@ -222,25 +222,25 @@
222
222
  "type": "module",
223
223
  "dependencies": {
224
224
  "@iconify-json/flat-color-icons": "^1.2.3",
225
- "@iconify-json/simple-icons": "^1.2.64",
225
+ "@iconify-json/simple-icons": "^1.2.66",
226
226
  "@iconify-json/circle-flags": "^1.2.10",
227
227
  "@inox-tools/runtime-logger": "^0.7.0",
228
228
  "@nanostores/i18n": "^1.2.2",
229
229
  "@nanostores/persistent": "^1.2.0",
230
230
  "@outerbase/sdk-transform": "^1.0.8",
231
- "@studiocms/ui": "^1.0.0",
232
- "@withstudiocms/cli-kit": "^0.1.0",
231
+ "@studiocms/ui": "^1.0.1",
233
232
  "ace-builds": "^1.43.5",
234
233
  "astro-integration-kit": "^0.19.1",
235
234
  "boxen": "^8.0.1",
236
235
  "chalk": "^5.6.2",
237
- "diff": "^8.0.2",
236
+ "diff": "^8.0.3",
238
237
  "dompurify": "^3.3.1",
239
238
  "dotenv": "^17.2.3",
240
239
  "fuse.js": "^7.1.0",
241
240
  "jose": "^6.1.3",
242
241
  "micromark": "^4.0.2",
243
242
  "micromark-extension-gfm": "^3.0.0",
243
+ "micromatch": "^4.0.8",
244
244
  "magicast": "^0.5.1",
245
245
  "mdast-util-to-markdown": "^2.1.2",
246
246
  "mrmime": "^2.0.1",
@@ -251,31 +251,44 @@
251
251
  "tinyglobby": "^0.2.15",
252
252
  "ultrahtml": "^1.6.0",
253
253
  "web-vitals": "^5.1.0",
254
- "@withstudiocms/internal_helpers": "0.1.0",
255
- "@withstudiocms/auth-kit": "0.1.0",
256
- "@withstudiocms/component-registry": "0.1.0",
257
- "@withstudiocms/config-utils": "0.1.0",
258
- "@withstudiocms/effect": "0.1.0",
259
- "@withstudiocms/template-lang": "0.1.0",
260
- "@withstudiocms/kysely": "0.1.0",
261
- "@withstudiocms/sdk": "0.1.0"
254
+ "@withstudiocms/internal_helpers": "^0.1.0",
255
+ "@withstudiocms/auth-kit": "^0.1.2",
256
+ "@withstudiocms/config-utils": "^0.1.0",
257
+ "@withstudiocms/component-registry": "^0.1.2",
258
+ "@withstudiocms/cli-kit": "^0.2.0",
259
+ "@withstudiocms/effect": "^0.2.0",
260
+ "@withstudiocms/kysely": "^0.2.0",
261
+ "@withstudiocms/template-lang": "^0.1.0",
262
+ "@withstudiocms/sdk": "^0.2.0"
262
263
  },
263
264
  "devDependencies": {
264
265
  "@types/mdast": "^4.0.4",
266
+ "@types/micromatch": "^4.0.10",
265
267
  "@types/node": "^22.0.0",
266
268
  "@types/semver": "^7.7.1",
267
269
  "@types/three": "0.169.0",
268
270
  "@types/pg": "^8.16.0",
269
- "typescript": "^5.9.3"
271
+ "typescript": "^5.9.3",
272
+ "vite": "^6.3.4"
270
273
  },
271
274
  "peerDependencies": {
275
+ "@libsql/client": "^0.15.15",
272
276
  "astro": "^5.12.9",
273
277
  "effect": "^3.19.14",
274
- "vite": "^6.3.4",
275
- "@libsql/client": "^0.15.15",
276
- "pg": "^8.16.3",
278
+ "pg": "^8.17.1",
277
279
  "mysql2": "^3.16.0"
278
280
  },
281
+ "peerDependenciesMeta": {
282
+ "@libsql/client": {
283
+ "optional": true
284
+ },
285
+ "pg": {
286
+ "optional": true
287
+ },
288
+ "mysql2": {
289
+ "optional": true
290
+ }
291
+ },
279
292
  "scripts": {
280
293
  "build": "buildkit build 'src/**/*.{ts,astro,css,json,png,webp,woff2,stub.js,d.ts}' --tsconfig=tsconfig.build.json",
281
294
  "dev": "buildkit dev 'src/**/*.{ts,astro,css,json,png,webp,woff2,stub.js,d.ts}'",