emdash 0.0.1 → 0.0.2

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 (33) hide show
  1. package/dist/astro/index.mjs +41 -38
  2. package/dist/astro/index.mjs.map +1 -1
  3. package/dist/astro/middleware.mjs +1 -1
  4. package/dist/cli/index.mjs +1 -1
  5. package/dist/cli/index.mjs.map +1 -1
  6. package/dist/db/index.mjs +1 -1
  7. package/dist/index.mjs +1 -1
  8. package/dist/{runner-B-u2F2b6.mjs → runner-C0hCbYnD.mjs} +15 -15
  9. package/dist/runner-C0hCbYnD.mjs.map +1 -0
  10. package/dist/storage/local.d.mts.map +1 -1
  11. package/dist/storage/local.mjs.map +1 -1
  12. package/dist/storage/s3.d.mts.map +1 -1
  13. package/dist/storage/s3.mjs.map +1 -1
  14. package/package.json +5 -5
  15. package/src/api/handlers/device-flow.ts +1 -4
  16. package/src/api/handlers/oauth-clients.ts +1 -5
  17. package/src/astro/integration/index.ts +1 -1
  18. package/src/astro/integration/vite-config.ts +8 -1
  19. package/src/astro/routes/api/admin/comments/[id]/status.ts +1 -5
  20. package/src/astro/routes/api/auth/passkey/register/verify.ts +1 -3
  21. package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +1 -5
  22. package/src/astro/routes/api/import/wordpress/analyze.ts +1 -1
  23. package/src/astro/routes/api/import/wordpress/media.ts +1 -1
  24. package/src/astro/routes/api/revisions/[revisionId]/restore.ts +1 -5
  25. package/src/astro/routes/api/widget-areas/[name]/reorder.ts +1 -5
  26. package/src/cli/commands/publish.ts +1 -3
  27. package/src/cli/commands/seed.ts +1 -3
  28. package/src/database/migrations/001_initial.ts +39 -5
  29. package/src/database/migrations/032_rate_limits.ts +1 -4
  30. package/src/mcp/server.ts +2 -2
  31. package/src/storage/local.ts +1 -5
  32. package/src/storage/s3.ts +2 -10
  33. package/dist/runner-B-u2F2b6.mjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emdash",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Astro-native CMS with WordPress migration support",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -178,9 +178,9 @@
178
178
  "ulidx": "^2.4.1",
179
179
  "upng-js": "^2.1.0",
180
180
  "zod": "^4.3.5",
181
- "@emdash-cms/gutenberg-to-portable-text": "0.0.1",
181
+ "@emdash-cms/admin": "0.0.1",
182
182
  "@emdash-cms/auth": "0.0.1",
183
- "@emdash-cms/admin": "0.0.1"
183
+ "@emdash-cms/gutenberg-to-portable-text": "0.0.1"
184
184
  },
185
185
  "optionalDependencies": {
186
186
  "@libsql/kysely-libsql": "^0.4.0",
@@ -212,10 +212,10 @@
212
212
  },
213
213
  "repository": {
214
214
  "type": "git",
215
- "url": "git+https://github.com/cloudflare/emdash.git",
215
+ "url": "git+https://github.com/emdash-cms/emdash.git",
216
216
  "directory": "packages/core"
217
217
  },
218
- "homepage": "https://github.com/cloudflare/emdash",
218
+ "homepage": "https://github.com/emdash-cms/emdash",
219
219
  "keywords": [
220
220
  "astro",
221
221
  "cms",
@@ -664,10 +664,7 @@ export async function handleTokenRevoke(
664
664
 
665
665
  if (row.token_type === "refresh") {
666
666
  // Revoke refresh token and all its access tokens
667
- await db
668
- .deleteFrom("_emdash_oauth_tokens")
669
- .where("refresh_token_hash", "=", hash)
670
- .execute();
667
+ await db.deleteFrom("_emdash_oauth_tokens").where("refresh_token_hash", "=", hash).execute();
671
668
  await db.deleteFrom("_emdash_oauth_tokens").where("token_hash", "=", hash).execute();
672
669
  } else {
673
670
  // Revoke just the access token
@@ -236,11 +236,7 @@ export async function handleOAuthClientUpdate(
236
236
  updates.scopes = input.scopes ? JSON.stringify(input.scopes) : "";
237
237
  }
238
238
 
239
- await db
240
- .updateTable("_emdash_oauth_clients")
241
- .set(updates)
242
- .where("id", "=", clientId)
243
- .execute();
239
+ await db.updateTable("_emdash_oauth_clients").set(updates).where("id", "=", clientId).execute();
244
240
 
245
241
  // Fetch the updated row
246
242
  const updated = await db
@@ -42,7 +42,7 @@ const cyan = (s: string) => `\x1b[36m${s}\x1b[39m`;
42
42
  function printBanner(_logger: AstroIntegrationLogger): void {
43
43
  const banner = `
44
44
 
45
- ${bold(cyan("E C L I P T I C"))}
45
+ ${bold(cyan("E M D A S H "))}
46
46
  `;
47
47
  console.log(banner);
48
48
  }
@@ -269,6 +269,13 @@ export function createViteConfig(
269
269
  // Vite discovers them one-by-one on first request, causing workerd
270
270
  // to enter "worker cancelled" state on cold cache.
271
271
  optimizeDeps: {
272
+ // Exclude EmDash virtual modules from esbuild's dependency
273
+ // scan. These are resolved by the Vite plugin at transform time,
274
+ // but esbuild encounters them when crawling emdash's dist files
275
+ // during pre-bundling and can't resolve them. Vite's exclude
276
+ // uses prefix matching (id.startsWith(m + "/")), so
277
+ // "virtual:emdash" matches all "virtual:emdash/*" imports.
278
+ exclude: ["virtual:emdash"],
272
279
  include: [
273
280
  // EmDash direct deps
274
281
  "emdash > @portabletext/toolkit",
@@ -322,7 +329,7 @@ export function createViteConfig(
322
329
  include: useSource
323
330
  ? ["@astrojs/react/client.js"]
324
331
  : ["@emdash-cms/admin", "@astrojs/react/client.js"],
325
- exclude: cloudflare ? [] : NODE_NATIVE_EXTERNALS,
332
+ exclude: cloudflare ? ["virtual:emdash"] : [...NODE_NATIVE_EXTERNALS, "virtual:emdash"],
326
333
  },
327
334
  };
328
335
  }
@@ -95,11 +95,7 @@ export const PUT: APIRoute = async ({ params, request, locals }) => {
95
95
  if (newStatus === "approved" && previousStatus !== "approved" && emdash.email) {
96
96
  try {
97
97
  const adminBaseUrl = await getSiteBaseUrl(emdash.db, request);
98
- const content = await lookupContentAuthor(
99
- emdash.db,
100
- updated.collection,
101
- updated.contentId,
102
- );
98
+ const content = await lookupContentAuthor(emdash.db, updated.collection, updated.contentId);
103
99
  if (content?.author) {
104
100
  await sendCommentNotification({
105
101
  email: emdash.email,
@@ -71,9 +71,7 @@ export const POST: APIRoute = async ({ request, locals }) => {
71
71
  // Get passkey name - prefer body.name, then check stored pending name
72
72
  let passKeyName: string | undefined = body.name ?? undefined;
73
73
  if (!passKeyName) {
74
- const pending = await optionsRepo.get<{ name?: string }>(
75
- `emdash:passkey_pending:${user.id}`,
76
- );
74
+ const pending = await optionsRepo.get<{ name?: string }>(`emdash:passkey_pending:${user.id}`);
77
75
  if (pending?.name) {
78
76
  passKeyName = pending.name;
79
77
  }
@@ -59,11 +59,7 @@ export const POST: APIRoute = async ({ params, request, locals, cache }) => {
59
59
  const denied = requireOwnerPerm(user, authorId, "content:publish_own", "content:publish_any");
60
60
  if (denied) return denied;
61
61
 
62
- const result = await emdash.handleContentSchedule(
63
- collection,
64
- resolvedId ?? id,
65
- body.scheduledAt,
66
- );
62
+ const result = await emdash.handleContentSchedule(collection, resolvedId ?? id, body.scheduledAt);
67
63
 
68
64
  if (!result.success) return unwrapResult(result);
69
65
 
@@ -8,8 +8,8 @@
8
8
  */
9
9
 
10
10
  import type { APIRoute } from "astro";
11
- import mime from "mime/lite";
12
11
  import { parseWxrString, SchemaRegistry, type WxrData } from "emdash";
12
+ import mime from "mime/lite";
13
13
 
14
14
  import { requirePerm } from "#api/authorize.js";
15
15
  import { apiError, apiSuccess, handleError } from "#api/error.js";
@@ -11,8 +11,8 @@
11
11
  import * as path from "node:path";
12
12
 
13
13
  import type { APIRoute } from "astro";
14
- import mime from "mime/lite";
15
14
  import { MediaRepository, computeContentHash } from "emdash";
15
+ import mime from "mime/lite";
16
16
  import { ulid } from "ulidx";
17
17
 
18
18
  import { requirePerm } from "#api/authorize.js";
@@ -15,11 +15,7 @@ export const POST: APIRoute = async ({ params, locals }) => {
15
15
  const { emdash, user } = locals;
16
16
  const revisionId = params.revisionId!;
17
17
 
18
- if (
19
- !emdash?.handleRevisionRestore ||
20
- !emdash?.handleRevisionGet ||
21
- !emdash?.handleContentGet
22
- ) {
18
+ if (!emdash?.handleRevisionRestore || !emdash?.handleRevisionGet || !emdash?.handleContentGet) {
23
19
  return apiError("NOT_CONFIGURED", "EmDash not configured", 500);
24
20
  }
25
21
 
@@ -57,11 +57,7 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
57
57
  // Update sort_order for each widget
58
58
  await Promise.all(
59
59
  body.widgetIds.map((id, index) =>
60
- db
61
- .updateTable("_emdash_widgets")
62
- .set({ sort_order: index })
63
- .where("id", "=", id)
64
- .execute(),
60
+ db.updateTable("_emdash_widgets").set({ sort_order: index }).where("id", "=", id).execute(),
65
61
  ),
66
62
  );
67
63
 
@@ -418,9 +418,7 @@ export const publishCommand = defineCommand({
418
418
  process.exit(1);
419
419
  }
420
420
  } catch {
421
- consola.error(
422
- "No dist/ directory found. Run `emdash plugin bundle` first or use --build.",
423
- );
421
+ consola.error("No dist/ directory found. Run `emdash plugin bundle` first or use --build.");
424
422
  process.exit(1);
425
423
  }
426
424
  }
@@ -135,9 +135,7 @@ export const seedCommand = defineCommand({
135
135
  const seedPath = await resolveSeedPath(cwd, args.path);
136
136
  if (!seedPath) {
137
137
  consola.error("No seed file found");
138
- consola.info(
139
- "Provide a path, create .emdash/seed.json, or set emdash.seed in package.json",
140
- );
138
+ consola.info("Provide a path, create .emdash/seed.json, or set emdash.seed in package.json");
141
139
  process.exit(1);
142
140
  }
143
141
 
@@ -14,6 +14,7 @@ export async function up(db: Kysely<unknown>): Promise<void> {
14
14
  // References entries in ec_* tables by collection + entry_id
15
15
  await db.schema
16
16
  .createTable("revisions")
17
+ .ifNotExists()
17
18
  .addColumn("id", "text", (col) => col.primaryKey())
18
19
  .addColumn("collection", "text", (col) => col.notNull()) // e.g., 'posts'
19
20
  .addColumn("entry_id", "text", (col) => col.notNull()) // ID in the ec_* table
@@ -24,6 +25,7 @@ export async function up(db: Kysely<unknown>): Promise<void> {
24
25
 
25
26
  await db.schema
26
27
  .createIndex("idx_revisions_entry")
28
+ .ifNotExists()
27
29
  .on("revisions")
28
30
  .columns(["collection", "entry_id"])
29
31
  .execute();
@@ -31,6 +33,7 @@ export async function up(db: Kysely<unknown>): Promise<void> {
31
33
  // Taxonomies
32
34
  await db.schema
33
35
  .createTable("taxonomies")
36
+ .ifNotExists()
34
37
  .addColumn("id", "text", (col) => col.primaryKey())
35
38
  .addColumn("name", "text", (col) => col.notNull())
36
39
  .addColumn("slug", "text", (col) => col.notNull())
@@ -43,11 +46,17 @@ export async function up(db: Kysely<unknown>): Promise<void> {
43
46
  )
44
47
  .execute();
45
48
 
46
- await db.schema.createIndex("idx_taxonomies_name").on("taxonomies").column("name").execute();
49
+ await db.schema
50
+ .createIndex("idx_taxonomies_name")
51
+ .ifNotExists()
52
+ .on("taxonomies")
53
+ .column("name")
54
+ .execute();
47
55
 
48
56
  // Content-Taxonomy junction - references entries in ec_* tables
49
57
  await db.schema
50
58
  .createTable("content_taxonomies")
59
+ .ifNotExists()
51
60
  .addColumn("collection", "text", (col) => col.notNull()) // e.g., 'posts'
52
61
  .addColumn("entry_id", "text", (col) => col.notNull()) // ID in the ec_* table
53
62
  .addColumn("taxonomy_id", "text", (col) => col.notNull())
@@ -64,6 +73,7 @@ export async function up(db: Kysely<unknown>): Promise<void> {
64
73
  // Media
65
74
  await db.schema
66
75
  .createTable("media")
76
+ .ifNotExists()
67
77
  .addColumn("id", "text", (col) => col.primaryKey())
68
78
  .addColumn("filename", "text", (col) => col.notNull())
69
79
  .addColumn("mime_type", "text", (col) => col.notNull())
@@ -80,6 +90,7 @@ export async function up(db: Kysely<unknown>): Promise<void> {
80
90
 
81
91
  await db.schema
82
92
  .createIndex("idx_media_content_hash")
93
+ .ifNotExists()
83
94
  .on("media")
84
95
  .column("content_hash")
85
96
  .execute();
@@ -87,6 +98,7 @@ export async function up(db: Kysely<unknown>): Promise<void> {
87
98
  // Users
88
99
  await db.schema
89
100
  .createTable("users")
101
+ .ifNotExists()
90
102
  .addColumn("id", "text", (col) => col.primaryKey())
91
103
  .addColumn("email", "text", (col) => col.notNull().unique())
92
104
  .addColumn("password_hash", "text", (col) => col.notNull())
@@ -97,11 +109,17 @@ export async function up(db: Kysely<unknown>): Promise<void> {
97
109
  .addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db)))
98
110
  .execute();
99
111
 
100
- await db.schema.createIndex("idx_users_email").on("users").column("email").execute();
112
+ await db.schema
113
+ .createIndex("idx_users_email")
114
+ .ifNotExists()
115
+ .on("users")
116
+ .column("email")
117
+ .execute();
101
118
 
102
119
  // Options (key-value store)
103
120
  await db.schema
104
121
  .createTable("options")
122
+ .ifNotExists()
105
123
  .addColumn("name", "text", (col) => col.primaryKey())
106
124
  .addColumn("value", "text", (col) => col.notNull())
107
125
  .execute();
@@ -109,6 +127,7 @@ export async function up(db: Kysely<unknown>): Promise<void> {
109
127
  // Audit logs (security events)
110
128
  await db.schema
111
129
  .createTable("audit_logs")
130
+ .ifNotExists()
112
131
  .addColumn("id", "text", (col) => col.primaryKey())
113
132
  .addColumn("timestamp", "text", (col) => col.defaultTo(currentTimestamp(db)))
114
133
  .addColumn("actor_id", "text")
@@ -120,9 +139,24 @@ export async function up(db: Kysely<unknown>): Promise<void> {
120
139
  .addColumn("status", "text")
121
140
  .execute();
122
141
 
123
- await db.schema.createIndex("idx_audit_actor").on("audit_logs").column("actor_id").execute();
124
- await db.schema.createIndex("idx_audit_action").on("audit_logs").column("action").execute();
125
- await db.schema.createIndex("idx_audit_timestamp").on("audit_logs").column("timestamp").execute();
142
+ await db.schema
143
+ .createIndex("idx_audit_actor")
144
+ .ifNotExists()
145
+ .on("audit_logs")
146
+ .column("actor_id")
147
+ .execute();
148
+ await db.schema
149
+ .createIndex("idx_audit_action")
150
+ .ifNotExists()
151
+ .on("audit_logs")
152
+ .column("action")
153
+ .execute();
154
+ await db.schema
155
+ .createIndex("idx_audit_timestamp")
156
+ .ifNotExists()
157
+ .on("audit_logs")
158
+ .column("timestamp")
159
+ .execute();
126
160
  }
127
161
 
128
162
  export async function down(db: Kysely<unknown>): Promise<void> {
@@ -27,10 +27,7 @@ export async function up(db: Kysely<unknown>): Promise<void> {
27
27
  .execute();
28
28
 
29
29
  // ── Device code polling tracking ─────────────────────────────────
30
- await db.schema
31
- .alterTable("_emdash_device_codes")
32
- .addColumn("last_polled_at", "text")
33
- .execute();
30
+ await db.schema.alterTable("_emdash_device_codes").addColumn("last_polled_at", "text").execute();
34
31
  }
35
32
 
36
33
  export async function down(db: Kysely<unknown>): Promise<void> {
package/src/mcp/server.ts CHANGED
@@ -9,10 +9,10 @@
9
9
  * The handlers instance is passed per-request via authInfo on the transport.
10
10
  */
11
11
 
12
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
- import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
14
12
  import type { Permission, RoleLevel } from "@emdash-cms/auth";
15
13
  import { canActOnOwn, Role } from "@emdash-cms/auth";
14
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
15
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
16
16
  import { z } from "zod";
17
17
 
18
18
  import type { EmDashHandlers } from "../astro/types.js";
@@ -103,11 +103,7 @@ export class LocalStorage implements Storage {
103
103
  size: buffer.length,
104
104
  };
105
105
  } catch (error) {
106
- throw new EmDashStorageError(
107
- `Failed to upload file: ${options.key}`,
108
- "UPLOAD_FAILED",
109
- error,
110
- );
106
+ throw new EmDashStorageError(`Failed to upload file: ${options.key}`, "UPLOAD_FAILED", error);
111
107
  }
112
108
  }
113
109
 
package/src/storage/s3.ts CHANGED
@@ -97,11 +97,7 @@ export class S3Storage implements Storage {
97
97
  size: body.length,
98
98
  };
99
99
  } catch (error) {
100
- throw new EmDashStorageError(
101
- `Failed to upload file: ${options.key}`,
102
- "UPLOAD_FAILED",
103
- error,
104
- );
100
+ throw new EmDashStorageError(`Failed to upload file: ${options.key}`, "UPLOAD_FAILED", error);
105
101
  }
106
102
  }
107
103
 
@@ -166,11 +162,7 @@ export class S3Storage implements Storage {
166
162
  if (hasErrorName(error) && error.name === "NotFound") {
167
163
  return false;
168
164
  }
169
- throw new EmDashStorageError(
170
- `Failed to check file existence: ${key}`,
171
- "HEAD_FAILED",
172
- error,
173
- );
165
+ throw new EmDashStorageError(`Failed to check file existence: ${key}`, "HEAD_FAILED", error);
174
166
  }
175
167
  }
176
168