emdash 0.1.1 → 0.3.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/dist/{apply-kC39ev1Z.mjs → apply-Bqoekfbe.mjs} +57 -10
- package/dist/apply-Bqoekfbe.mjs.map +1 -0
- package/dist/astro/index.d.mts +23 -9
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +90 -25
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +3 -3
- package/dist/astro/middleware/auth.d.mts.map +1 -1
- package/dist/astro/middleware/auth.mjs +126 -55
- package/dist/astro/middleware/auth.mjs.map +1 -1
- package/dist/astro/middleware/redirect.mjs +2 -2
- package/dist/astro/middleware/request-context.mjs +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +80 -41
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/types.d.mts +27 -6
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{byline-CL847F26.mjs → byline-BGj9p9Ht.mjs} +53 -31
- package/dist/byline-BGj9p9Ht.mjs.map +1 -0
- package/dist/{bylines-C2a-2TGt.mjs → bylines-BihaoIDY.mjs} +12 -10
- package/dist/{bylines-C2a-2TGt.mjs.map → bylines-BihaoIDY.mjs.map} +1 -1
- package/dist/cli/index.mjs +17 -14
- package/dist/cli/index.mjs.map +1 -1
- package/dist/{config-CKE8p9xM.mjs → config-Cq8H0SfX.mjs} +2 -10
- package/dist/{config-CKE8p9xM.mjs.map → config-Cq8H0SfX.mjs.map} +1 -1
- package/dist/{content-D6C2WsZC.mjs → content-BsBoyj8G.mjs} +35 -5
- package/dist/content-BsBoyj8G.mjs.map +1 -0
- package/dist/db/index.mjs +2 -2
- package/dist/{default-Cyi4aAxu.mjs → default-WYlzADZL.mjs} +1 -1
- package/dist/{default-Cyi4aAxu.mjs.map → default-WYlzADZL.mjs.map} +1 -1
- package/dist/{dialect-helpers-B9uSp2GJ.mjs → dialect-helpers-DhTzaUxP.mjs} +4 -1
- package/dist/dialect-helpers-DhTzaUxP.mjs.map +1 -0
- package/dist/{error-Cxz0tQeO.mjs → error-DrxtnGPg.mjs} +1 -1
- package/dist/{error-Cxz0tQeO.mjs.map → error-DrxtnGPg.mjs.map} +1 -1
- package/dist/{index-CLBc4gw-.d.mts → index-Cff7AimE.d.mts} +77 -15
- package/dist/index-Cff7AimE.d.mts.map +1 -0
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +19 -19
- package/dist/{load-yOOlckBj.mjs → load-Veizk2cT.mjs} +1 -1
- package/dist/{load-yOOlckBj.mjs.map → load-Veizk2cT.mjs.map} +1 -1
- package/dist/{loader-fz8Q_3EO.mjs → loader-BmYdf3Dr.mjs} +4 -2
- package/dist/loader-BmYdf3Dr.mjs.map +1 -0
- package/dist/{manifest-schema-CL8DWO9b.mjs → manifest-schema-CuMio1A9.mjs} +1 -1
- package/dist/{manifest-schema-CL8DWO9b.mjs.map → manifest-schema-CuMio1A9.mjs.map} +1 -1
- package/dist/media/local-runtime.d.mts +4 -4
- package/dist/page/index.d.mts +10 -1
- package/dist/page/index.d.mts.map +1 -1
- package/dist/page/index.mjs +8 -4
- package/dist/page/index.mjs.map +1 -1
- package/dist/plugins/adapt-sandbox-entry.d.mts +3 -3
- package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
- package/dist/{query-BVYN0PJ6.mjs → query-sesiOndV.mjs} +20 -8
- package/dist/{query-BVYN0PJ6.mjs.map → query-sesiOndV.mjs.map} +1 -1
- package/dist/{redirect-DIfIni3r.mjs → redirect-DUAk-Yl_.mjs} +9 -2
- package/dist/redirect-DUAk-Yl_.mjs.map +1 -0
- package/dist/{registry-BNYQKX_d.mjs → registry-DU18yVo0.mjs} +14 -4
- package/dist/registry-DU18yVo0.mjs.map +1 -0
- package/dist/{runner-BraqvGYk.mjs → runner-Biufrii2.mjs} +157 -132
- package/dist/runner-Biufrii2.mjs.map +1 -0
- package/dist/runner-EAtf0ZIe.d.mts.map +1 -1
- package/dist/runtime.d.mts +3 -3
- package/dist/runtime.mjs +2 -2
- package/dist/{search-C1gg67nN.mjs → search-BXB-jfu2.mjs} +241 -109
- package/dist/search-BXB-jfu2.mjs.map +1 -0
- package/dist/seed/index.d.mts +1 -1
- package/dist/seed/index.mjs +10 -10
- package/dist/seo/index.d.mts +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +11 -3
- package/dist/storage/s3.d.mts.map +1 -1
- package/dist/storage/s3.mjs +76 -15
- package/dist/storage/s3.mjs.map +1 -1
- package/dist/{tokens-DpgrkrXK.mjs → tokens-DrB-W6Q-.mjs} +1 -1
- package/dist/{tokens-DpgrkrXK.mjs.map → tokens-DrB-W6Q-.mjs.map} +1 -1
- package/dist/{types-BRuPJGdV.d.mts → types-BbsYgi_R.d.mts} +3 -1
- package/dist/types-BbsYgi_R.d.mts.map +1 -0
- package/dist/{types-CUBbjgmP.mjs → types-Bec-r_3_.mjs} +1 -1
- package/dist/types-Bec-r_3_.mjs.map +1 -0
- package/dist/{types-DaNLHo_T.d.mts → types-C1-PVaS_.d.mts} +14 -6
- package/dist/types-C1-PVaS_.d.mts.map +1 -0
- package/dist/types-CMMN0pNg.mjs.map +1 -1
- package/dist/{types-BQo5JS0J.d.mts → types-CaKte3hR.d.mts} +78 -6
- package/dist/types-CaKte3hR.d.mts.map +1 -0
- package/dist/{types-CiA5Gac0.mjs → types-DuNbGKjF.mjs} +1 -1
- package/dist/{types-CiA5Gac0.mjs.map → types-DuNbGKjF.mjs.map} +1 -1
- package/dist/{validate-_rsF-Dx_.mjs → validate-CXnRKfJK.mjs} +2 -2
- package/dist/{validate-_rsF-Dx_.mjs.map → validate-CXnRKfJK.mjs.map} +1 -1
- package/dist/{validate-CqRJb_xU.mjs → validate-VPnKoIzW.mjs} +11 -11
- package/dist/{validate-CqRJb_xU.mjs.map → validate-VPnKoIzW.mjs.map} +1 -1
- package/dist/{validate-HtxZeaBi.d.mts → validate-bfg9OR6N.d.mts} +2 -2
- package/dist/{validate-HtxZeaBi.d.mts.map → validate-bfg9OR6N.d.mts.map} +1 -1
- package/dist/version-REAapfsU.mjs +7 -0
- package/dist/version-REAapfsU.mjs.map +1 -0
- package/package.json +6 -6
- package/src/api/csrf.ts +13 -2
- package/src/api/handlers/content.ts +7 -0
- package/src/api/handlers/dashboard.ts +4 -8
- package/src/api/handlers/device-flow.ts +55 -37
- package/src/api/handlers/index.ts +6 -1
- package/src/api/handlers/redirects.ts +95 -3
- package/src/api/handlers/seo.ts +48 -21
- package/src/api/public-url.ts +84 -0
- package/src/api/schemas/content.ts +2 -2
- package/src/api/schemas/menus.ts +12 -2
- package/src/api/schemas/redirects.ts +1 -0
- package/src/astro/integration/index.ts +30 -7
- package/src/astro/integration/routes.ts +13 -2
- package/src/astro/integration/runtime.ts +7 -5
- package/src/astro/integration/vite-config.ts +55 -9
- package/src/astro/middleware/auth.ts +60 -56
- package/src/astro/middleware/csp.ts +25 -0
- package/src/astro/middleware.ts +31 -3
- package/src/astro/routes/PluginRegistry.tsx +8 -2
- package/src/astro/routes/admin.astro +7 -2
- package/src/astro/routes/api/admin/users/[id]/disable.ts +18 -12
- package/src/astro/routes/api/admin/users/[id]/index.ts +26 -5
- package/src/astro/routes/api/auth/invite/complete.ts +3 -2
- package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +2 -1
- package/src/astro/routes/api/auth/oauth/[provider].ts +2 -1
- package/src/astro/routes/api/auth/passkey/options.ts +3 -2
- package/src/astro/routes/api/auth/passkey/register/options.ts +3 -2
- package/src/astro/routes/api/auth/passkey/register/verify.ts +3 -2
- package/src/astro/routes/api/auth/passkey/verify.ts +3 -2
- package/src/astro/routes/api/auth/signup/complete.ts +3 -2
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +2 -0
- package/src/astro/routes/api/content/[collection]/index.ts +31 -3
- package/src/astro/routes/api/import/wordpress/execute.ts +9 -0
- package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +2 -0
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +10 -0
- package/src/astro/routes/api/manifest.ts +4 -1
- package/src/astro/routes/api/media/providers/[providerId]/[itemId].ts +7 -2
- package/src/astro/routes/api/oauth/authorize.ts +12 -7
- package/src/astro/routes/api/oauth/device/code.ts +5 -1
- package/src/astro/routes/api/setup/admin-verify.ts +3 -2
- package/src/astro/routes/api/setup/admin.ts +3 -2
- package/src/astro/routes/api/setup/dev-bypass.ts +2 -1
- package/src/astro/routes/api/setup/index.ts +3 -2
- package/src/astro/routes/api/snapshot.ts +2 -1
- package/src/astro/routes/api/themes/preview.ts +2 -1
- package/src/astro/routes/api/well-known/auth.ts +1 -0
- package/src/astro/routes/api/well-known/oauth-authorization-server.ts +3 -2
- package/src/astro/routes/api/well-known/oauth-protected-resource.ts +3 -2
- package/src/astro/routes/robots.txt.ts +5 -1
- package/src/astro/routes/sitemap-[collection].xml.ts +104 -0
- package/src/astro/routes/sitemap.xml.ts +18 -23
- package/src/astro/storage/adapters.ts +19 -5
- package/src/astro/storage/types.ts +12 -4
- package/src/astro/types.ts +28 -1
- package/src/auth/passkey-config.ts +6 -10
- package/src/bylines/index.ts +13 -10
- package/src/cli/commands/login.ts +5 -2
- package/src/components/InlinePortableTextEditor.tsx +5 -3
- package/src/content/converters/portable-text-to-prosemirror.ts +50 -2
- package/src/database/dialect-helpers.ts +3 -0
- package/src/database/migrations/034_published_at_index.ts +29 -0
- package/src/database/migrations/runner.ts +2 -0
- package/src/database/repositories/byline.ts +48 -42
- package/src/database/repositories/content.ts +28 -1
- package/src/database/repositories/options.ts +9 -3
- package/src/database/repositories/redirect.ts +13 -0
- package/src/database/repositories/seo.ts +34 -17
- package/src/database/repositories/types.ts +2 -0
- package/src/database/validate.ts +10 -10
- package/src/emdash-runtime.ts +66 -19
- package/src/import/index.ts +1 -1
- package/src/import/sources/wxr.ts +45 -2
- package/src/index.ts +10 -1
- package/src/loader.ts +2 -0
- package/src/mcp/server.ts +85 -5
- package/src/menus/index.ts +6 -1
- package/src/page/context.ts +13 -1
- package/src/page/jsonld.ts +10 -6
- package/src/page/seo-contributions.ts +1 -1
- package/src/plugins/context.ts +145 -35
- package/src/plugins/manager.ts +12 -0
- package/src/plugins/types.ts +80 -4
- package/src/query.ts +18 -0
- package/src/redirects/loops.ts +318 -0
- package/src/schema/registry.ts +8 -0
- package/src/search/fts-manager.ts +4 -0
- package/src/settings/index.ts +64 -0
- package/src/storage/s3.ts +94 -25
- package/src/storage/types.ts +13 -5
- package/src/utils/chunks.ts +17 -0
- package/src/utils/slugify.ts +11 -0
- package/src/version.ts +12 -0
- package/dist/apply-kC39ev1Z.mjs.map +0 -1
- package/dist/byline-CL847F26.mjs.map +0 -1
- package/dist/content-D6C2WsZC.mjs.map +0 -1
- package/dist/dialect-helpers-B9uSp2GJ.mjs.map +0 -1
- package/dist/index-CLBc4gw-.d.mts.map +0 -1
- package/dist/loader-fz8Q_3EO.mjs.map +0 -1
- package/dist/redirect-DIfIni3r.mjs.map +0 -1
- package/dist/registry-BNYQKX_d.mjs.map +0 -1
- package/dist/runner-BraqvGYk.mjs.map +0 -1
- package/dist/search-C1gg67nN.mjs.map +0 -1
- package/dist/types-BQo5JS0J.d.mts.map +0 -1
- package/dist/types-BRuPJGdV.d.mts.map +0 -1
- package/dist/types-CUBbjgmP.mjs.map +0 -1
- package/dist/types-DaNLHo_T.d.mts.map +0 -1
- /package/src/astro/routes/api/media/file/{[key].ts → [...key].ts} +0 -0
package/dist/astro/types.d.mts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { f as MediaProvider, p as MediaProviderCapabilities } from "../placeholder-SvFCKbz_.mjs";
|
|
2
2
|
import { t as Database } from "../types-DRjfYOEv.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { Dr as SandboxRunner, Hr as MediaResponse, Pi as MediaItem, Vr as MediaListResponse, an as EmailPipeline, bi as ContentListResponse, dn as EmDashConfig, on as HookPipeline, xi as ContentResponse } from "../index-Cff7AimE.mjs";
|
|
4
4
|
import "../runner-EAtf0ZIe.mjs";
|
|
5
|
-
import { r as ContentItem } from "../types-
|
|
6
|
-
import {
|
|
7
|
-
import "../validate-
|
|
8
|
-
import { d as Storage } from "../types-
|
|
5
|
+
import { r as ContentItem } from "../types-BbsYgi_R.mjs";
|
|
6
|
+
import { G as PublicPageContext, J as ResolvedPlugin, O as PageMetadataContribution, T as PageFragmentContribution, at as Element } from "../types-CaKte3hR.mjs";
|
|
7
|
+
import "../validate-bfg9OR6N.mjs";
|
|
8
|
+
import { d as Storage } from "../types-C1-PVaS_.mjs";
|
|
9
9
|
import "../index.mjs";
|
|
10
10
|
import { Kysely } from "kysely";
|
|
11
11
|
|
|
@@ -24,10 +24,16 @@ interface ManifestCollection {
|
|
|
24
24
|
label?: string;
|
|
25
25
|
required?: boolean;
|
|
26
26
|
widget?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Field options. Two shapes:
|
|
29
|
+
* - Legacy enum: `Array<{ value, label }>` for select / multiSelect widgets
|
|
30
|
+
* - Plugin widgets: `Record<string, unknown>` for arbitrary per-field config
|
|
31
|
+
* (e.g. a checkbox grid receiving its column definitions)
|
|
32
|
+
*/
|
|
27
33
|
options?: Array<{
|
|
28
34
|
value: string;
|
|
29
35
|
label: string;
|
|
30
|
-
}>;
|
|
36
|
+
}> | Record<string, unknown>;
|
|
31
37
|
}>;
|
|
32
38
|
}
|
|
33
39
|
/**
|
|
@@ -83,6 +89,7 @@ type ManifestAuthMode = string;
|
|
|
83
89
|
*/
|
|
84
90
|
interface EmDashManifest {
|
|
85
91
|
version: string;
|
|
92
|
+
commit?: string;
|
|
86
93
|
hash: string;
|
|
87
94
|
collections: Record<string, ManifestCollection>;
|
|
88
95
|
plugins: Record<string, ManifestPlugin>;
|
|
@@ -107,6 +114,16 @@ interface EmDashManifest {
|
|
|
107
114
|
locales: string[];
|
|
108
115
|
prefixDefaultLocale?: boolean;
|
|
109
116
|
};
|
|
117
|
+
/**
|
|
118
|
+
* Taxonomy definitions for the admin sidebar.
|
|
119
|
+
*/
|
|
120
|
+
taxonomies: Array<{
|
|
121
|
+
name: string;
|
|
122
|
+
label: string;
|
|
123
|
+
labelSingular?: string;
|
|
124
|
+
hierarchical: boolean;
|
|
125
|
+
collections: string[];
|
|
126
|
+
}>;
|
|
110
127
|
/**
|
|
111
128
|
* Whether the plugin marketplace is configured.
|
|
112
129
|
* When true, the admin UI can show marketplace browse/install features.
|
|
@@ -160,6 +177,8 @@ interface EmDashHandlers {
|
|
|
160
177
|
authorId?: string;
|
|
161
178
|
locale?: string;
|
|
162
179
|
translationOf?: string;
|
|
180
|
+
createdAt?: string | null;
|
|
181
|
+
publishedAt?: string | null;
|
|
163
182
|
}) => Promise<HandlerResponse>;
|
|
164
183
|
handleContentUpdate: (collection: string, id: string, body: {
|
|
165
184
|
data?: Record<string, unknown>;
|
|
@@ -245,6 +264,8 @@ interface EmDashHandlers {
|
|
|
245
264
|
getSandboxRunner: () => SandboxRunner | null;
|
|
246
265
|
syncMarketplacePlugins: () => Promise<void>;
|
|
247
266
|
setPluginStatus: (pluginId: string, status: "active" | "inactive") => Promise<void>;
|
|
267
|
+
collectPageMetadata: (page: PublicPageContext) => Promise<PageMetadataContribution[]>;
|
|
268
|
+
collectPageFragments: (page: PublicPageContext) => Promise<PageFragmentContribution[]>;
|
|
248
269
|
}
|
|
249
270
|
//#endregion
|
|
250
271
|
export { type ContentItem, type ContentListResponse, type ContentResponse, type Database, EmDashHandlers, EmDashManifest, HandlerResponse, ManifestAuthMode, ManifestCollection, ManifestPlugin, type MediaItem, type MediaListResponse, type MediaResponse, type Storage };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../src/astro/types.ts"],"mappings":";;;;;;;;;;;;;;;UAyBiB,kBAAA;EAChB,KAAA;EACA,aAAA;EACA,QAAA;EACA,MAAA;EACA,UAAA;EACA,MAAA,EAAQ,MAAA;IAGN,IAAA;IACA,KAAA;IACA,QAAA;IACA,MAAA;
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../src/astro/types.ts"],"mappings":"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"}
|
|
@@ -1,9 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { t as validateIdentifier } from "./validate-VPnKoIzW.mjs";
|
|
2
|
+
import { s as listTablesLike } from "./dialect-helpers-DhTzaUxP.mjs";
|
|
3
3
|
import { n as decodeCursor, r as encodeCursor } from "./types-CMMN0pNg.mjs";
|
|
4
4
|
import { sql } from "kysely";
|
|
5
5
|
import { ulid } from "ulidx";
|
|
6
6
|
|
|
7
|
+
//#region src/utils/chunks.ts
|
|
8
|
+
/**
|
|
9
|
+
* Split an array into chunks of at most `size` elements.
|
|
10
|
+
*
|
|
11
|
+
* Used to keep SQL `IN (?, ?, …)` clauses within Cloudflare D1's
|
|
12
|
+
* bound-parameter limit (~100 per statement).
|
|
13
|
+
*/
|
|
14
|
+
function chunks(arr, size) {
|
|
15
|
+
if (arr.length === 0) return [];
|
|
16
|
+
const result = [];
|
|
17
|
+
for (let i = 0; i < arr.length; i += size) result.push(arr.slice(i, i + size));
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
/** Conservative default chunk size for SQL IN clauses (well within D1's limit). */
|
|
21
|
+
const SQL_BATCH_SIZE = 50;
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
7
24
|
//#region src/database/repositories/byline.ts
|
|
8
25
|
function rowToByline(row) {
|
|
9
26
|
return {
|
|
@@ -133,31 +150,34 @@ var BylineRepository = class {
|
|
|
133
150
|
async getContentBylinesMany(collectionSlug, contentIds) {
|
|
134
151
|
const result = /* @__PURE__ */ new Map();
|
|
135
152
|
if (contentIds.length === 0) return result;
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
"cb.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
153
|
+
const uniqueContentIds = [...new Set(contentIds)];
|
|
154
|
+
for (const chunk of chunks(uniqueContentIds, SQL_BATCH_SIZE)) {
|
|
155
|
+
const rows = await this.db.selectFrom("_emdash_content_bylines as cb").innerJoin("_emdash_bylines as b", "b.id", "cb.byline_id").select([
|
|
156
|
+
"cb.content_id as content_id",
|
|
157
|
+
"cb.sort_order as sort_order",
|
|
158
|
+
"cb.role_label as role_label",
|
|
159
|
+
"b.id as id",
|
|
160
|
+
"b.slug as slug",
|
|
161
|
+
"b.display_name as display_name",
|
|
162
|
+
"b.bio as bio",
|
|
163
|
+
"b.avatar_media_id as avatar_media_id",
|
|
164
|
+
"b.website_url as website_url",
|
|
165
|
+
"b.user_id as user_id",
|
|
166
|
+
"b.is_guest as is_guest",
|
|
167
|
+
"b.created_at as created_at",
|
|
168
|
+
"b.updated_at as updated_at"
|
|
169
|
+
]).where("cb.collection_slug", "=", collectionSlug).where("cb.content_id", "in", chunk).orderBy("cb.sort_order", "asc").execute();
|
|
170
|
+
for (const row of rows) {
|
|
171
|
+
const contentId = row.content_id;
|
|
172
|
+
const credit = {
|
|
173
|
+
byline: rowToByline(row),
|
|
174
|
+
sortOrder: row.sort_order,
|
|
175
|
+
roleLabel: row.role_label
|
|
176
|
+
};
|
|
177
|
+
const existing = result.get(contentId);
|
|
178
|
+
if (existing) existing.push(credit);
|
|
179
|
+
else result.set(contentId, [credit]);
|
|
180
|
+
}
|
|
161
181
|
}
|
|
162
182
|
return result;
|
|
163
183
|
}
|
|
@@ -168,8 +188,10 @@ var BylineRepository = class {
|
|
|
168
188
|
async findByUserIds(userIds) {
|
|
169
189
|
const result = /* @__PURE__ */ new Map();
|
|
170
190
|
if (userIds.length === 0) return result;
|
|
171
|
-
const
|
|
172
|
-
|
|
191
|
+
for (const chunk of chunks(userIds, SQL_BATCH_SIZE)) {
|
|
192
|
+
const rows = await this.db.selectFrom("_emdash_bylines").selectAll().where("user_id", "in", chunk).execute();
|
|
193
|
+
for (const row of rows) if (row.user_id) result.set(row.user_id, rowToByline(row));
|
|
194
|
+
}
|
|
173
195
|
return result;
|
|
174
196
|
}
|
|
175
197
|
async setContentBylines(collectionSlug, contentId, inputBylines) {
|
|
@@ -209,5 +231,5 @@ var BylineRepository = class {
|
|
|
209
231
|
};
|
|
210
232
|
|
|
211
233
|
//#endregion
|
|
212
|
-
export { BylineRepository as t };
|
|
213
|
-
//# sourceMappingURL=byline-
|
|
234
|
+
export { SQL_BATCH_SIZE as n, chunks as r, BylineRepository as t };
|
|
235
|
+
//# sourceMappingURL=byline-BGj9p9Ht.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"byline-BGj9p9Ht.mjs","names":[],"sources":["../src/utils/chunks.ts","../src/database/repositories/byline.ts"],"sourcesContent":["/**\n * Split an array into chunks of at most `size` elements.\n *\n * Used to keep SQL `IN (?, ?, …)` clauses within Cloudflare D1's\n * bound-parameter limit (~100 per statement).\n */\nexport function chunks<T>(arr: T[], size: number): T[][] {\n\tif (arr.length === 0) return [];\n\tconst result: T[][] = [];\n\tfor (let i = 0; i < arr.length; i += size) {\n\t\tresult.push(arr.slice(i, i + size));\n\t}\n\treturn result;\n}\n\n/** Conservative default chunk size for SQL IN clauses (well within D1's limit). */\nexport const SQL_BATCH_SIZE = 50;\n","import { sql, type Kysely, type Selectable } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { chunks, SQL_BATCH_SIZE } from \"../../utils/chunks.js\";\nimport { listTablesLike } from \"../dialect-helpers.js\";\nimport type { BylineTable, Database } from \"../types.js\";\nimport { validateIdentifier } from \"../validate.js\";\nimport {\n\tdecodeCursor,\n\tencodeCursor,\n\ttype BylineSummary,\n\ttype ContentBylineCredit,\n\ttype FindManyResult,\n} from \"./types.js\";\n\ntype BylineRow = Selectable<BylineTable>;\n\nexport interface CreateBylineInput {\n\tslug: string;\n\tdisplayName: string;\n\tbio?: string | null;\n\tavatarMediaId?: string | null;\n\twebsiteUrl?: string | null;\n\tuserId?: string | null;\n\tisGuest?: boolean;\n}\n\nexport interface UpdateBylineInput {\n\tslug?: string;\n\tdisplayName?: string;\n\tbio?: string | null;\n\tavatarMediaId?: string | null;\n\twebsiteUrl?: string | null;\n\tuserId?: string | null;\n\tisGuest?: boolean;\n}\n\nexport interface ContentBylineInput {\n\tbylineId: string;\n\troleLabel?: string | null;\n}\n\nfunction rowToByline(row: BylineRow): BylineSummary {\n\treturn {\n\t\tid: row.id,\n\t\tslug: row.slug,\n\t\tdisplayName: row.display_name,\n\t\tbio: row.bio,\n\t\tavatarMediaId: row.avatar_media_id,\n\t\twebsiteUrl: row.website_url,\n\t\tuserId: row.user_id,\n\t\tisGuest: row.is_guest === 1,\n\t\tcreatedAt: row.created_at,\n\t\tupdatedAt: row.updated_at,\n\t};\n}\n\nexport class BylineRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\tasync findById(id: string): Promise<BylineSummary | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\t\treturn row ? rowToByline(row) : null;\n\t}\n\n\tasync findBySlug(slug: string): Promise<BylineSummary | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t.executeTakeFirst();\n\t\treturn row ? rowToByline(row) : null;\n\t}\n\n\tasync findByUserId(userId: string): Promise<BylineSummary | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.where(\"user_id\", \"=\", userId)\n\t\t\t.executeTakeFirst();\n\t\treturn row ? rowToByline(row) : null;\n\t}\n\n\tasync findMany(options?: {\n\t\tsearch?: string;\n\t\tisGuest?: boolean;\n\t\tuserId?: string;\n\t\tcursor?: string;\n\t\tlimit?: number;\n\t}): Promise<FindManyResult<BylineSummary>> {\n\t\tconst limit = Math.min(Math.max(options?.limit ?? 50, 1), 100);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(\"created_at\", \"desc\")\n\t\t\t.orderBy(\"id\", \"desc\")\n\t\t\t.limit(limit + 1);\n\n\t\tif (options?.search) {\n\t\t\tconst escaped = options.search\n\t\t\t\t.replaceAll(\"\\\\\", \"\\\\\\\\\")\n\t\t\t\t.replaceAll(\"%\", \"\\\\%\")\n\t\t\t\t.replaceAll(\"_\", \"\\\\_\");\n\t\t\tconst term = `%${escaped}%`;\n\t\t\tquery = query.where((eb) =>\n\t\t\t\teb.or([eb(\"display_name\", \"like\", term), eb(\"slug\", \"like\", term)]),\n\t\t\t);\n\t\t}\n\n\t\tif (options?.isGuest !== undefined) {\n\t\t\tquery = query.where(\"is_guest\", \"=\", options.isGuest ? 1 : 0);\n\t\t}\n\n\t\tif (options?.userId !== undefined) {\n\t\t\tquery = query.where(\"user_id\", \"=\", options.userId);\n\t\t}\n\n\t\tif (options?.cursor) {\n\t\t\tconst decoded = decodeCursor(options.cursor);\n\t\t\tif (decoded) {\n\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\teb.or([\n\t\t\t\t\t\teb(\"created_at\", \"<\", decoded.orderValue),\n\t\t\t\t\t\teb.and([eb(\"created_at\", \"=\", decoded.orderValue), eb(\"id\", \"<\", decoded.id)]),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst rows = await query.execute();\n\t\tconst items = rows.slice(0, limit).map(rowToByline);\n\t\tconst result: FindManyResult<BylineSummary> = { items };\n\n\t\tif (rows.length > limit) {\n\t\t\tconst last = items.at(-1);\n\t\t\tif (last) {\n\t\t\t\tresult.nextCursor = encodeCursor(last.createdAt, last.id);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tasync create(input: CreateBylineInput): Promise<BylineSummary> {\n\t\tconst id = ulid();\n\t\tconst now = new Date().toISOString();\n\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_bylines\")\n\t\t\t.values({\n\t\t\t\tid,\n\t\t\t\tslug: input.slug,\n\t\t\t\tdisplay_name: input.displayName,\n\t\t\t\tbio: input.bio ?? null,\n\t\t\t\tavatar_media_id: input.avatarMediaId ?? null,\n\t\t\t\twebsite_url: input.websiteUrl ?? null,\n\t\t\t\tuser_id: input.userId ?? null,\n\t\t\t\tis_guest: input.isGuest ? 1 : 0,\n\t\t\t\tcreated_at: now,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.execute();\n\n\t\tconst byline = await this.findById(id);\n\t\tif (!byline) {\n\t\t\tthrow new Error(\"Failed to create byline\");\n\t\t}\n\t\treturn byline;\n\t}\n\n\tasync update(id: string, input: UpdateBylineInput): Promise<BylineSummary | null> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return null;\n\n\t\tconst updates: Record<string, unknown> = {\n\t\t\tupdated_at: new Date().toISOString(),\n\t\t};\n\n\t\tif (input.slug !== undefined) updates.slug = input.slug;\n\t\tif (input.displayName !== undefined) updates.display_name = input.displayName;\n\t\tif (input.bio !== undefined) updates.bio = input.bio;\n\t\tif (input.avatarMediaId !== undefined) updates.avatar_media_id = input.avatarMediaId;\n\t\tif (input.websiteUrl !== undefined) updates.website_url = input.websiteUrl;\n\t\tif (input.userId !== undefined) updates.user_id = input.userId;\n\t\tif (input.isGuest !== undefined) updates.is_guest = input.isGuest ? 1 : 0;\n\n\t\tawait this.db.updateTable(\"_emdash_bylines\").set(updates).where(\"id\", \"=\", id).execute();\n\t\treturn await this.findById(id);\n\t}\n\n\tasync delete(id: string): Promise<boolean> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return false;\n\n\t\tawait this.db.transaction().execute(async (trx) => {\n\t\t\tawait trx.deleteFrom(\"_emdash_content_bylines\").where(\"byline_id\", \"=\", id).execute();\n\n\t\t\tawait trx.deleteFrom(\"_emdash_bylines\").where(\"id\", \"=\", id).execute();\n\n\t\t\tconst tableNames = await listTablesLike(trx, \"ec_%\");\n\t\t\tfor (const tableName of tableNames) {\n\t\t\t\tvalidateIdentifier(tableName, \"content table\");\n\t\t\t\tawait sql`\n\t\t\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\t\t\tSET primary_byline_id = NULL\n\t\t\t\t\tWHERE primary_byline_id = ${id}\n\t\t\t\t`.execute(trx);\n\t\t\t}\n\t\t});\n\n\t\treturn true;\n\t}\n\n\tasync getContentBylines(\n\t\tcollectionSlug: string,\n\t\tcontentId: string,\n\t): Promise<ContentBylineCredit[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_content_bylines as cb\")\n\t\t\t.innerJoin(\"_emdash_bylines as b\", \"b.id\", \"cb.byline_id\")\n\t\t\t.select([\n\t\t\t\t\"cb.sort_order as sort_order\",\n\t\t\t\t\"cb.role_label as role_label\",\n\t\t\t\t\"b.id as id\",\n\t\t\t\t\"b.slug as slug\",\n\t\t\t\t\"b.display_name as display_name\",\n\t\t\t\t\"b.bio as bio\",\n\t\t\t\t\"b.avatar_media_id as avatar_media_id\",\n\t\t\t\t\"b.website_url as website_url\",\n\t\t\t\t\"b.user_id as user_id\",\n\t\t\t\t\"b.is_guest as is_guest\",\n\t\t\t\t\"b.created_at as created_at\",\n\t\t\t\t\"b.updated_at as updated_at\",\n\t\t\t])\n\t\t\t.where(\"cb.collection_slug\", \"=\", collectionSlug)\n\t\t\t.where(\"cb.content_id\", \"=\", contentId)\n\t\t\t.orderBy(\"cb.sort_order\", \"asc\")\n\t\t\t.execute();\n\n\t\treturn rows.map((row) => ({\n\t\t\tbyline: rowToByline(row),\n\t\t\tsortOrder: row.sort_order,\n\t\t\troleLabel: row.role_label,\n\t\t}));\n\t}\n\n\t/**\n\t * Batch-fetch byline credits for multiple content items in a single query.\n\t * Returns a Map keyed by contentId.\n\t */\n\tasync getContentBylinesMany(\n\t\tcollectionSlug: string,\n\t\tcontentIds: string[],\n\t): Promise<Map<string, ContentBylineCredit[]>> {\n\t\tconst result = new Map<string, ContentBylineCredit[]>();\n\t\tif (contentIds.length === 0) return result;\n\n\t\tconst uniqueContentIds = [...new Set(contentIds)];\n\t\tfor (const chunk of chunks(uniqueContentIds, SQL_BATCH_SIZE)) {\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_content_bylines as cb\")\n\t\t\t\t.innerJoin(\"_emdash_bylines as b\", \"b.id\", \"cb.byline_id\")\n\t\t\t\t.select([\n\t\t\t\t\t\"cb.content_id as content_id\",\n\t\t\t\t\t\"cb.sort_order as sort_order\",\n\t\t\t\t\t\"cb.role_label as role_label\",\n\t\t\t\t\t\"b.id as id\",\n\t\t\t\t\t\"b.slug as slug\",\n\t\t\t\t\t\"b.display_name as display_name\",\n\t\t\t\t\t\"b.bio as bio\",\n\t\t\t\t\t\"b.avatar_media_id as avatar_media_id\",\n\t\t\t\t\t\"b.website_url as website_url\",\n\t\t\t\t\t\"b.user_id as user_id\",\n\t\t\t\t\t\"b.is_guest as is_guest\",\n\t\t\t\t\t\"b.created_at as created_at\",\n\t\t\t\t\t\"b.updated_at as updated_at\",\n\t\t\t\t])\n\t\t\t\t.where(\"cb.collection_slug\", \"=\", collectionSlug)\n\t\t\t\t.where(\"cb.content_id\", \"in\", chunk)\n\t\t\t\t.orderBy(\"cb.sort_order\", \"asc\")\n\t\t\t\t.execute();\n\n\t\t\tfor (const row of rows) {\n\t\t\t\tconst contentId = row.content_id;\n\t\t\t\tconst credit: ContentBylineCredit = {\n\t\t\t\t\tbyline: rowToByline(row),\n\t\t\t\t\tsortOrder: row.sort_order,\n\t\t\t\t\troleLabel: row.role_label,\n\t\t\t\t};\n\t\t\t\tconst existing = result.get(contentId);\n\t\t\t\tif (existing) {\n\t\t\t\t\texisting.push(credit);\n\t\t\t\t} else {\n\t\t\t\t\tresult.set(contentId, [credit]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Batch-fetch byline profiles linked to user IDs in a single query.\n\t * Returns a Map keyed by userId.\n\t */\n\tasync findByUserIds(userIds: string[]): Promise<Map<string, BylineSummary>> {\n\t\tconst result = new Map<string, BylineSummary>();\n\t\tif (userIds.length === 0) return result;\n\n\t\tfor (const chunk of chunks(userIds, SQL_BATCH_SIZE)) {\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t\t.selectAll()\n\t\t\t\t.where(\"user_id\", \"in\", chunk)\n\t\t\t\t.execute();\n\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.user_id) {\n\t\t\t\t\tresult.set(row.user_id, rowToByline(row));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync setContentBylines(\n\t\tcollectionSlug: string,\n\t\tcontentId: string,\n\t\tinputBylines: ContentBylineInput[],\n\t): Promise<ContentBylineCredit[]> {\n\t\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\t\tconst tableName = `ec_${collectionSlug}`;\n\t\tvalidateIdentifier(tableName, \"content table\");\n\n\t\tconst seen = new Set<string>();\n\t\tconst bylines = inputBylines.filter((item) => {\n\t\t\tif (seen.has(item.bylineId)) return false;\n\t\t\tseen.add(item.bylineId);\n\t\t\treturn true;\n\t\t});\n\n\t\t// This method is expected to be called within a transaction context\n\t\t// (content handlers wrap in withTransaction, seed applies sequentially).\n\t\t// All operations use this.db directly -- callers are responsible for\n\t\t// wrapping in a transaction when atomicity is required.\n\t\tif (bylines.length > 0) {\n\t\t\tconst ids = bylines.map((item) => item.bylineId);\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t\t.select(\"id\")\n\t\t\t\t.where(\"id\", \"in\", ids)\n\t\t\t\t.execute();\n\t\t\tif (rows.length !== ids.length) {\n\t\t\t\tthrow new Error(\"One or more byline IDs do not exist\");\n\t\t\t}\n\t\t}\n\n\t\tawait this.db\n\t\t\t.deleteFrom(\"_emdash_content_bylines\")\n\t\t\t.where(\"collection_slug\", \"=\", collectionSlug)\n\t\t\t.where(\"content_id\", \"=\", contentId)\n\t\t\t.execute();\n\n\t\tfor (let i = 0; i < bylines.length; i++) {\n\t\t\tconst item = bylines[i];\n\t\t\tawait this.db\n\t\t\t\t.insertInto(\"_emdash_content_bylines\")\n\t\t\t\t.values({\n\t\t\t\t\tid: ulid(),\n\t\t\t\t\tcollection_slug: collectionSlug,\n\t\t\t\t\tcontent_id: contentId,\n\t\t\t\t\tbyline_id: item.bylineId,\n\t\t\t\t\tsort_order: i,\n\t\t\t\t\trole_label: item.roleLabel ?? null,\n\t\t\t\t\tcreated_at: new Date().toISOString(),\n\t\t\t\t})\n\t\t\t\t.execute();\n\t\t}\n\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET primary_byline_id = ${bylines[0]?.bylineId ?? null}\n\t\t\tWHERE id = ${contentId}\n\t\t`.execute(this.db);\n\n\t\treturn await this.getContentBylines(collectionSlug, contentId);\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-ClPoSABd.mjs";
|
|
2
|
-
import { t as validateIdentifier } from "./validate-
|
|
3
|
-
import { t as BylineRepository } from "./byline-
|
|
4
|
-
import { n as getDb } from "./loader-
|
|
2
|
+
import { t as validateIdentifier } from "./validate-VPnKoIzW.mjs";
|
|
3
|
+
import { n as SQL_BATCH_SIZE, r as chunks, t as BylineRepository } from "./byline-BGj9p9Ht.mjs";
|
|
4
|
+
import { n as getDb } from "./loader-BmYdf3Dr.mjs";
|
|
5
5
|
import { sql } from "kysely";
|
|
6
6
|
|
|
7
7
|
//#region src/bylines/index.ts
|
|
@@ -120,17 +120,19 @@ async function getBylinesForEntries(collection, entryIds) {
|
|
|
120
120
|
* Returns Map<entryId, authorId> (only entries with non-null author_id).
|
|
121
121
|
*/
|
|
122
122
|
async function getAuthorIds(db, collection, entryIds) {
|
|
123
|
+
validateIdentifier(collection, "collection");
|
|
123
124
|
const tableName = `ec_${collection}`;
|
|
124
|
-
validateIdentifier(tableName, "content table");
|
|
125
|
-
const result = await sql`
|
|
126
|
-
SELECT id, author_id FROM ${sql.ref(tableName)}
|
|
127
|
-
WHERE id IN (${sql.join(entryIds.map((id) => sql`${id}`))})
|
|
128
|
-
`.execute(db);
|
|
129
125
|
const map = /* @__PURE__ */ new Map();
|
|
130
|
-
for (const
|
|
126
|
+
for (const chunk of chunks(entryIds, SQL_BATCH_SIZE)) {
|
|
127
|
+
const result = await sql`
|
|
128
|
+
SELECT id, author_id FROM ${sql.ref(tableName)}
|
|
129
|
+
WHERE id IN (${sql.join(chunk.map((id) => sql`${id}`))})
|
|
130
|
+
`.execute(db);
|
|
131
|
+
for (const row of result.rows) if (row.author_id) map.set(row.id, row.author_id);
|
|
132
|
+
}
|
|
131
133
|
return map;
|
|
132
134
|
}
|
|
133
135
|
|
|
134
136
|
//#endregion
|
|
135
137
|
export { getByline as n, getBylineBySlug as r, bylines_exports as t };
|
|
136
|
-
//# sourceMappingURL=bylines-
|
|
138
|
+
//# sourceMappingURL=bylines-BihaoIDY.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bylines-
|
|
1
|
+
{"version":3,"file":"bylines-BihaoIDY.mjs","names":[],"sources":["../src/bylines/index.ts"],"sourcesContent":["/**\n * Runtime API for bylines\n *\n * Provides functions to query byline profiles and byline credits\n * associated with content entries. Follows the same pattern as\n * the taxonomies runtime API.\n */\n\nimport { sql } from \"kysely\";\n\nimport { BylineRepository } from \"../database/repositories/byline.js\";\nimport type { BylineSummary, ContentBylineCredit } from \"../database/repositories/types.js\";\nimport { validateIdentifier } from \"../database/validate.js\";\nimport { getDb } from \"../loader.js\";\nimport { chunks, SQL_BATCH_SIZE } from \"../utils/chunks.js\";\n\n/**\n * Get a byline by ID.\n *\n * @example\n * ```ts\n * import { getByline } from \"emdash\";\n *\n * const byline = await getByline(\"01HXYZ...\");\n * if (byline) {\n * console.log(byline.displayName);\n * }\n * ```\n */\nexport async function getByline(id: string): Promise<BylineSummary | null> {\n\tconst db = await getDb();\n\tconst repo = new BylineRepository(db);\n\treturn repo.findById(id);\n}\n\n/**\n * Get a byline by slug.\n *\n * @example\n * ```ts\n * import { getBylineBySlug } from \"emdash\";\n *\n * const byline = await getBylineBySlug(\"jane-doe\");\n * if (byline) {\n * console.log(byline.displayName); // \"Jane Doe\"\n * }\n * ```\n */\nexport async function getBylineBySlug(slug: string): Promise<BylineSummary | null> {\n\tconst db = await getDb();\n\tconst repo = new BylineRepository(db);\n\treturn repo.findBySlug(slug);\n}\n\n/**\n * Get byline credits for a single content entry.\n *\n * Returns explicit byline credits from the junction table. If none exist\n * but the entry has an `authorId`, falls back to the user-linked byline\n * (marked as source: \"inferred\").\n *\n * @example\n * ```ts\n * import { getEntryBylines } from \"emdash\";\n *\n * const bylines = await getEntryBylines(\"posts\", post.data.id);\n * for (const credit of bylines) {\n * console.log(credit.byline.displayName, credit.roleLabel);\n * }\n * ```\n */\nexport async function getEntryBylines(\n\tcollection: string,\n\tentryId: string,\n): Promise<ContentBylineCredit[]> {\n\tvalidateIdentifier(collection, \"collection\");\n\tconst db = await getDb();\n\tconst repo = new BylineRepository(db);\n\n\tconst explicit = await repo.getContentBylines(collection, entryId);\n\tif (explicit.length > 0) {\n\t\treturn explicit.map((c) => ({ ...c, source: \"explicit\" as const }));\n\t}\n\n\t// Fallback: look up user-linked byline from author_id\n\tconst authorId = await getAuthorId(db, collection, entryId);\n\tif (authorId) {\n\t\tconst fallback = await repo.findByUserId(authorId);\n\t\tif (fallback) {\n\t\t\treturn [{ byline: fallback, sortOrder: 0, roleLabel: null, source: \"inferred\" }];\n\t\t}\n\t}\n\n\treturn [];\n}\n\n/**\n * Batch-fetch byline credits for multiple content entries in a single query.\n *\n * This is more efficient than calling getEntryBylines for each entry\n * when you need bylines for a list of entries (e.g., a blog index page).\n *\n * @param collection - The collection slug (e.g., \"posts\")\n * @param entryIds - Array of entry IDs\n * @returns Map from entry ID to array of byline credits\n *\n * @example\n * ```ts\n * import { getBylinesForEntries, getEmDashCollection } from \"emdash\";\n *\n * const { entries } = await getEmDashCollection(\"posts\");\n * const ids = entries.map(e => e.data.id);\n * const bylinesMap = await getBylinesForEntries(\"posts\", ids);\n *\n * for (const entry of entries) {\n * const bylines = bylinesMap.get(entry.data.id) ?? [];\n * // render bylines\n * }\n * ```\n */\nexport async function getBylinesForEntries(\n\tcollection: string,\n\tentryIds: string[],\n): Promise<Map<string, ContentBylineCredit[]>> {\n\tvalidateIdentifier(collection, \"collection\");\n\tconst result = new Map<string, ContentBylineCredit[]>();\n\n\t// Initialize all entry IDs with empty arrays\n\tfor (const id of entryIds) {\n\t\tresult.set(id, []);\n\t}\n\n\tif (entryIds.length === 0) {\n\t\treturn result;\n\t}\n\n\tconst db = await getDb();\n\tconst repo = new BylineRepository(db);\n\n\t// 1. Batch fetch all explicit byline credits\n\tconst bylinesMap = await repo.getContentBylinesMany(collection, entryIds);\n\n\t// 2. Collect entry IDs that need fallback lookup\n\tconst fallbackEntryIds: string[] = [];\n\tconst needsFallback: Map<string, string> = new Map(); // entryId -> authorId\n\n\tfor (const id of entryIds) {\n\t\tif (!bylinesMap.has(id)) {\n\t\t\t// Need to check author_id for this entry — but we only have the IDs,\n\t\t\t// so batch-fetch them from the content table\n\t\t\tfallbackEntryIds.push(id);\n\t\t}\n\t}\n\n\t// Batch-fetch author_ids for entries that need fallback\n\tif (fallbackEntryIds.length > 0) {\n\t\tconst authorMap = await getAuthorIds(db, collection, fallbackEntryIds);\n\t\tfor (const [entryId, authorId] of authorMap) {\n\t\t\tneedsFallback.set(entryId, authorId);\n\t\t}\n\t}\n\n\t// 3. Batch fetch user-linked bylines for fallback\n\tconst uniqueAuthorIds = [...new Set(needsFallback.values())];\n\tconst authorBylineMap = await repo.findByUserIds(uniqueAuthorIds);\n\n\t// 4. Assign results\n\tfor (const id of entryIds) {\n\t\tconst explicit = bylinesMap.get(id);\n\t\tif (explicit && explicit.length > 0) {\n\t\t\tresult.set(\n\t\t\t\tid,\n\t\t\t\texplicit.map((c) => ({ ...c, source: \"explicit\" as const })),\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst authorId = needsFallback.get(id);\n\t\tif (authorId) {\n\t\t\tconst fallback = authorBylineMap.get(authorId);\n\t\t\tif (fallback) {\n\t\t\t\tresult.set(id, [{ byline: fallback, sortOrder: 0, roleLabel: null, source: \"inferred\" }]);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Already initialized with empty array\n\t}\n\n\treturn result;\n}\n\n/**\n * Look up the author_id for a single content entry.\n * Uses raw SQL since we need dynamic table names.\n */\nasync function getAuthorId(\n\tdb: Awaited<ReturnType<typeof getDb>>,\n\tcollection: string,\n\tentryId: string,\n): Promise<string | null> {\n\tvalidateIdentifier(collection, \"collection\");\n\tconst tableName = `ec_${collection}`;\n\n\tconst result = await sql<{ author_id: string | null }>`\n\t\tSELECT author_id FROM ${sql.ref(tableName)}\n\t\tWHERE id = ${entryId}\n\t\tLIMIT 1\n\t`.execute(db);\n\n\treturn result.rows[0]?.author_id ?? null;\n}\n\n/**\n * Batch-fetch author_ids for multiple content entries.\n * Returns Map<entryId, authorId> (only entries with non-null author_id).\n */\nasync function getAuthorIds(\n\tdb: Awaited<ReturnType<typeof getDb>>,\n\tcollection: string,\n\tentryIds: string[],\n): Promise<Map<string, string>> {\n\tvalidateIdentifier(collection, \"collection\");\n\tconst tableName = `ec_${collection}`;\n\n\tconst map = new Map<string, string>();\n\tfor (const chunk of chunks(entryIds, SQL_BATCH_SIZE)) {\n\t\tconst result = await sql<{ id: string; author_id: string | null }>`\n\t\t\tSELECT id, author_id FROM ${sql.ref(tableName)}\n\t\t\tWHERE id IN (${sql.join(chunk.map((id) => sql`${id}`))})\n\t\t`.execute(db);\n\n\t\tfor (const row of result.rows) {\n\t\t\tif (row.author_id) {\n\t\t\t\tmap.set(row.id, row.author_id);\n\t\t\t}\n\t\t}\n\t}\n\treturn map;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,eAAsB,UAAU,IAA2C;AAG1E,QADa,IAAI,iBADN,MAAM,OAAO,CACa,CACzB,SAAS,GAAG;;;;;;;;;;;;;;;AAgBzB,eAAsB,gBAAgB,MAA6C;AAGlF,QADa,IAAI,iBADN,MAAM,OAAO,CACa,CACzB,WAAW,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;AAqE7B,eAAsB,qBACrB,YACA,UAC8C;AAC9C,oBAAmB,YAAY,aAAa;CAC5C,MAAM,yBAAS,IAAI,KAAoC;AAGvD,MAAK,MAAM,MAAM,SAChB,QAAO,IAAI,IAAI,EAAE,CAAC;AAGnB,KAAI,SAAS,WAAW,EACvB,QAAO;CAGR,MAAM,KAAK,MAAM,OAAO;CACxB,MAAM,OAAO,IAAI,iBAAiB,GAAG;CAGrC,MAAM,aAAa,MAAM,KAAK,sBAAsB,YAAY,SAAS;CAGzE,MAAM,mBAA6B,EAAE;CACrC,MAAM,gCAAqC,IAAI,KAAK;AAEpD,MAAK,MAAM,MAAM,SAChB,KAAI,CAAC,WAAW,IAAI,GAAG,CAGtB,kBAAiB,KAAK,GAAG;AAK3B,KAAI,iBAAiB,SAAS,GAAG;EAChC,MAAM,YAAY,MAAM,aAAa,IAAI,YAAY,iBAAiB;AACtE,OAAK,MAAM,CAAC,SAAS,aAAa,UACjC,eAAc,IAAI,SAAS,SAAS;;CAKtC,MAAM,kBAAkB,CAAC,GAAG,IAAI,IAAI,cAAc,QAAQ,CAAC,CAAC;CAC5D,MAAM,kBAAkB,MAAM,KAAK,cAAc,gBAAgB;AAGjE,MAAK,MAAM,MAAM,UAAU;EAC1B,MAAM,WAAW,WAAW,IAAI,GAAG;AACnC,MAAI,YAAY,SAAS,SAAS,GAAG;AACpC,UAAO,IACN,IACA,SAAS,KAAK,OAAO;IAAE,GAAG;IAAG,QAAQ;IAAqB,EAAE,CAC5D;AACD;;EAGD,MAAM,WAAW,cAAc,IAAI,GAAG;AACtC,MAAI,UAAU;GACb,MAAM,WAAW,gBAAgB,IAAI,SAAS;AAC9C,OAAI,UAAU;AACb,WAAO,IAAI,IAAI,CAAC;KAAE,QAAQ;KAAU,WAAW;KAAG,WAAW;KAAM,QAAQ;KAAY,CAAC,CAAC;AACzF;;;;AAOH,QAAO;;;;;;AA4BR,eAAe,aACd,IACA,YACA,UAC+B;AAC/B,oBAAmB,YAAY,aAAa;CAC5C,MAAM,YAAY,MAAM;CAExB,MAAM,sBAAM,IAAI,KAAqB;AACrC,MAAK,MAAM,SAAS,OAAO,UAAU,eAAe,EAAE;EACrD,MAAM,SAAS,MAAM,GAA6C;+BACrC,IAAI,IAAI,UAAU,CAAC;kBAChC,IAAI,KAAK,MAAM,KAAK,OAAO,GAAG,GAAG,KAAK,CAAC,CAAC;IACtD,QAAQ,GAAG;AAEb,OAAK,MAAM,OAAO,OAAO,KACxB,KAAI,IAAI,UACP,KAAI,IAAI,IAAI,IAAI,IAAI,UAAU;;AAIjC,QAAO"}
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as __exportAll } from "../chunk-ClPoSABd.mjs";
|
|
3
3
|
import { n as createDatabase } from "../connection-B4zVnQIa.mjs";
|
|
4
|
-
import { s as listTablesLike } from "../dialect-helpers-
|
|
5
|
-
import { r as runMigrations, t as getMigrationStatus } from "../runner-
|
|
6
|
-
import { t as ContentRepository } from "../content-
|
|
4
|
+
import { s as listTablesLike } from "../dialect-helpers-DhTzaUxP.mjs";
|
|
5
|
+
import { r as runMigrations, t as getMigrationStatus } from "../runner-Biufrii2.mjs";
|
|
6
|
+
import { t as ContentRepository } from "../content-BsBoyj8G.mjs";
|
|
7
7
|
import { i as encodeBase64url } from "../base64-MBPo9ozB.mjs";
|
|
8
8
|
import "../types-CMMN0pNg.mjs";
|
|
9
9
|
import { t as MediaRepository } from "../media-DqHVh136.mjs";
|
|
10
|
-
import {
|
|
11
|
-
import { n as SchemaRegistry } from "../registry-
|
|
12
|
-
import "../redirect-
|
|
13
|
-
import "../byline-
|
|
14
|
-
import {
|
|
15
|
-
import "../loader-
|
|
16
|
-
import { i as pluginManifestSchema } from "../manifest-schema-
|
|
17
|
-
import { t as validateSeed } from "../validate-
|
|
10
|
+
import { f as OptionsRepository, p as TaxonomyRepository, t as applySeed } from "../apply-Bqoekfbe.mjs";
|
|
11
|
+
import { n as SchemaRegistry } from "../registry-DU18yVo0.mjs";
|
|
12
|
+
import "../redirect-DUAk-Yl_.mjs";
|
|
13
|
+
import "../byline-BGj9p9Ht.mjs";
|
|
14
|
+
import { r as isI18nEnabled } from "../config-Cq8H0SfX.mjs";
|
|
15
|
+
import "../loader-BmYdf3Dr.mjs";
|
|
16
|
+
import { i as pluginManifestSchema } from "../manifest-schema-CuMio1A9.mjs";
|
|
17
|
+
import { t as validateSeed } from "../validate-CXnRKfJK.mjs";
|
|
18
18
|
import { LocalStorage } from "../storage/local.mjs";
|
|
19
19
|
import { createHeaderAwareFetch, customHeadersInterceptor, getCachedAccessToken, isAccessRedirect, resolveCustomHeaders, runCloudflaredLogin } from "../client/cf-access.mjs";
|
|
20
20
|
import { EmDashClient } from "../client/index.mjs";
|
|
@@ -1587,7 +1587,7 @@ async function pollForToken(tokenEndpoint, deviceCode, interval, expiresIn, fetc
|
|
|
1587
1587
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
1588
1588
|
})
|
|
1589
1589
|
});
|
|
1590
|
-
if (res.ok) return await res.json();
|
|
1590
|
+
if (res.ok) return (await res.json()).data;
|
|
1591
1591
|
const body = await res.json();
|
|
1592
1592
|
if (body.error === "authorization_pending") continue;
|
|
1593
1593
|
if (body.error === "slow_down") {
|
|
@@ -1699,13 +1699,16 @@ const loginCommand = defineCommand({
|
|
|
1699
1699
|
"Content-Type": "application/json",
|
|
1700
1700
|
"X-EmDash-Request": "1"
|
|
1701
1701
|
},
|
|
1702
|
-
body: JSON.stringify({
|
|
1702
|
+
body: JSON.stringify({
|
|
1703
|
+
client_id: "emdash-cli",
|
|
1704
|
+
scope: "admin"
|
|
1705
|
+
})
|
|
1703
1706
|
});
|
|
1704
1707
|
if (!codeRes.ok) {
|
|
1705
1708
|
consola$1.error(`Failed to request device code: ${codeRes.status}`);
|
|
1706
1709
|
process.exit(2);
|
|
1707
1710
|
}
|
|
1708
|
-
const deviceCode = await codeRes.json();
|
|
1711
|
+
const deviceCode = (await codeRes.json()).data;
|
|
1709
1712
|
console.log();
|
|
1710
1713
|
consola$1.info(`Open your browser to:`);
|
|
1711
1714
|
console.log(` ${pc.cyan(pc.bold(deviceCode.verification_uri))}`);
|