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.
- package/dist/astro/index.mjs +41 -38
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware.mjs +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/db/index.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{runner-B-u2F2b6.mjs → runner-C0hCbYnD.mjs} +15 -15
- package/dist/runner-C0hCbYnD.mjs.map +1 -0
- package/dist/storage/local.d.mts.map +1 -1
- package/dist/storage/local.mjs.map +1 -1
- package/dist/storage/s3.d.mts.map +1 -1
- package/dist/storage/s3.mjs.map +1 -1
- package/package.json +5 -5
- package/src/api/handlers/device-flow.ts +1 -4
- package/src/api/handlers/oauth-clients.ts +1 -5
- package/src/astro/integration/index.ts +1 -1
- package/src/astro/integration/vite-config.ts +8 -1
- package/src/astro/routes/api/admin/comments/[id]/status.ts +1 -5
- package/src/astro/routes/api/auth/passkey/register/verify.ts +1 -3
- package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +1 -5
- package/src/astro/routes/api/import/wordpress/analyze.ts +1 -1
- package/src/astro/routes/api/import/wordpress/media.ts +1 -1
- package/src/astro/routes/api/revisions/[revisionId]/restore.ts +1 -5
- package/src/astro/routes/api/widget-areas/[name]/reorder.ts +1 -5
- package/src/cli/commands/publish.ts +1 -3
- package/src/cli/commands/seed.ts +1 -3
- package/src/database/migrations/001_initial.ts +39 -5
- package/src/database/migrations/032_rate_limits.ts +1 -4
- package/src/mcp/server.ts +2 -2
- package/src/storage/local.ts +1 -5
- package/src/storage/s3.ts +2 -10
- 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.
|
|
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/
|
|
181
|
+
"@emdash-cms/admin": "0.0.1",
|
|
182
182
|
"@emdash-cms/auth": "0.0.1",
|
|
183
|
-
"@emdash-cms/
|
|
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/
|
|
215
|
+
"url": "git+https://github.com/emdash-cms/emdash.git",
|
|
216
216
|
"directory": "packages/core"
|
|
217
217
|
},
|
|
218
|
-
"homepage": "https://github.com/
|
|
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
|
|
@@ -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
|
}
|
package/src/cli/commands/seed.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
124
|
-
|
|
125
|
-
|
|
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";
|
package/src/storage/local.ts
CHANGED
|
@@ -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
|
|