emdash 0.7.0 → 0.8.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/{adapters-Di31kZ28.d.mts → adapters-BKSf3T9R.d.mts} +1 -1
- package/dist/{adapters-Di31kZ28.d.mts.map → adapters-BKSf3T9R.d.mts.map} +1 -1
- package/dist/{apply-5uslYdUu.mjs → apply-x0eMK1lX.mjs} +18 -17
- package/dist/apply-x0eMK1lX.mjs.map +1 -0
- package/dist/astro/index.d.mts +6 -6
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +86 -15
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +5 -5
- package/dist/astro/middleware/auth.d.mts.map +1 -1
- package/dist/astro/middleware/auth.mjs +22 -2
- 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/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +259 -71
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/types.d.mts +16 -8
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{byline-C4OVd8b3.mjs → byline-Chbr2GoP.mjs} +3 -3
- package/dist/byline-Chbr2GoP.mjs.map +1 -0
- package/dist/{bylines-hPTW79hw.mjs → bylines-CRNsVG88.mjs} +4 -4
- package/dist/{bylines-hPTW79hw.mjs.map → bylines-CRNsVG88.mjs.map} +1 -1
- package/dist/cli/index.mjs +16 -12
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +1 -1
- package/dist/{content-D7J5y73J.mjs → content-BcQPYxdV.mjs} +13 -15
- package/dist/content-BcQPYxdV.mjs.map +1 -0
- package/dist/db/index.d.mts +3 -3
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{db-errors-D0UT85nC.mjs → db-errors-l1Qh2RPR.mjs} +1 -1
- package/dist/{db-errors-D0UT85nC.mjs.map → db-errors-l1Qh2RPR.mjs.map} +1 -1
- package/dist/{default-CME5YdZ3.mjs → default-DCVqE5ib.mjs} +1 -1
- package/dist/{default-CME5YdZ3.mjs.map → default-DCVqE5ib.mjs.map} +1 -1
- package/dist/{error-CiYn9yDu.mjs → error-zG5T1UGA.mjs} +1 -1
- package/dist/error-zG5T1UGA.mjs.map +1 -0
- package/dist/{index-De6_Xv3v.d.mts → index-DIb-CzNx.d.mts} +157 -14
- package/dist/index-DIb-CzNx.d.mts.map +1 -0
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +22 -20
- package/dist/{load-CBcmDIot.mjs → load-CyEoextb.mjs} +1 -1
- package/dist/{load-CBcmDIot.mjs.map → load-CyEoextb.mjs.map} +1 -1
- package/dist/{loader-DeiBJEMe.mjs → loader-CndGj8kM.mjs} +8 -6
- package/dist/loader-CndGj8kM.mjs.map +1 -0
- package/dist/{manifest-schema-V30qsMft.mjs → manifest-schema-DH9xhc6t.mjs} +13 -1
- package/dist/manifest-schema-DH9xhc6t.mjs.map +1 -0
- package/dist/media/index.d.mts +1 -1
- package/dist/media/local-runtime.d.mts +7 -7
- package/dist/media/local-runtime.mjs +2 -2
- package/dist/{media-DqHVh136.mjs → media-D8FbNsl0.mjs} +4 -7
- package/dist/media-D8FbNsl0.mjs.map +1 -0
- package/dist/{mode-CpNnGkPz.mjs → mode-BnAOqItE.mjs} +1 -1
- package/dist/mode-BnAOqItE.mjs.map +1 -0
- package/dist/page/index.d.mts +2 -2
- package/dist/placeholder-C-fk5hYI.mjs.map +1 -1
- package/dist/{placeholder-tzpqGWII.d.mts → placeholder-D29tWZ7o.d.mts} +1 -1
- package/dist/{placeholder-tzpqGWII.d.mts.map → placeholder-D29tWZ7o.d.mts.map} +1 -1
- package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
- package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
- package/dist/{query-g4Ug-9j9.mjs → query-fqEdLFms.mjs} +9 -9
- package/dist/{query-g4Ug-9j9.mjs.map → query-fqEdLFms.mjs.map} +1 -1
- package/dist/{redirect-CN0Rt9Ob.mjs → redirect-D_pshWdf.mjs} +4 -4
- package/dist/redirect-D_pshWdf.mjs.map +1 -0
- package/dist/{registry-Ci3WxVAr.mjs → registry-C3Mr0ODu.mjs} +33 -9
- package/dist/registry-C3Mr0ODu.mjs.map +1 -0
- package/dist/{request-cache-DiR961CV.mjs → request-cache-Ci7f5pBb.mjs} +1 -1
- package/dist/request-cache-Ci7f5pBb.mjs.map +1 -0
- package/dist/{runner-BR2xKwhn.d.mts → runner-OURCaApa.d.mts} +2 -2
- package/dist/{runner-BR2xKwhn.d.mts.map → runner-OURCaApa.d.mts.map} +1 -1
- package/dist/runtime.d.mts +6 -6
- package/dist/runtime.mjs +2 -2
- package/dist/{search-B0effn3j.mjs → search-BoZYFuUk.mjs} +227 -84
- package/dist/search-BoZYFuUk.mjs.map +1 -0
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +12 -12
- 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 +1 -1
- package/dist/storage/s3.d.mts.map +1 -1
- package/dist/storage/s3.mjs +4 -4
- package/dist/storage/s3.mjs.map +1 -1
- package/dist/{taxonomies-K2z0Uhnj.mjs → taxonomies-B4IAshV8.mjs} +5 -5
- package/dist/{taxonomies-K2z0Uhnj.mjs.map → taxonomies-B4IAshV8.mjs.map} +1 -1
- package/dist/{tokens-BFPFx3CA.mjs → tokens-D9vnZqYS.mjs} +1 -1
- package/dist/{tokens-BFPFx3CA.mjs.map → tokens-D9vnZqYS.mjs.map} +1 -1
- package/dist/{transport-BykRfpyy.mjs → transport-C9ugt2Nr.mjs} +1 -1
- package/dist/{transport-BykRfpyy.mjs.map → transport-C9ugt2Nr.mjs.map} +1 -1
- package/dist/{transport-H4Iwx7tC.d.mts → transport-CUnEL3Vs.d.mts} +1 -1
- package/dist/{transport-H4Iwx7tC.d.mts.map → transport-CUnEL3Vs.d.mts.map} +1 -1
- package/dist/types-BIgulNsW.mjs +68 -0
- package/dist/types-BIgulNsW.mjs.map +1 -0
- package/dist/{types-DDS4MxsT.mjs → types-Bm1dn-q3.mjs} +1 -1
- package/dist/{types-DDS4MxsT.mjs.map → types-Bm1dn-q3.mjs.map} +1 -1
- package/dist/{types-CnZYHyLW.d.mts → types-BmPPSUEx.d.mts} +1 -1
- package/dist/{types-CnZYHyLW.d.mts.map → types-BmPPSUEx.d.mts.map} +1 -1
- package/dist/{types-6CUZRrZP.d.mts → types-BrA0xf5I.d.mts} +24 -2
- package/dist/{types-6CUZRrZP.d.mts.map → types-BrA0xf5I.d.mts.map} +1 -1
- package/dist/{types-C2v0c34j.d.mts → types-CS8FIX7L.d.mts} +1 -1
- package/dist/{types-C2v0c34j.d.mts.map → types-CS8FIX7L.d.mts.map} +1 -1
- package/dist/{types-BH2L167P.mjs → types-CgqmmMJB.mjs} +1 -1
- package/dist/{types-BH2L167P.mjs.map → types-CgqmmMJB.mjs.map} +1 -1
- package/dist/{types-CFWjXmus.d.mts → types-DIMwPFub.d.mts} +1 -1
- package/dist/{types-CFWjXmus.d.mts.map → types-DIMwPFub.d.mts.map} +1 -1
- package/dist/{types-DgrIP0tF.d.mts → types-i36XcA_X.d.mts} +49 -6
- package/dist/types-i36XcA_X.d.mts.map +1 -0
- package/dist/{validate-CqsNItbt.mjs → validate-CxVsLehf.mjs} +2 -2
- package/dist/{validate-CqsNItbt.mjs.map → validate-CxVsLehf.mjs.map} +1 -1
- package/dist/{validate-kM8Pjuf7.d.mts → validate-DHxmpFJt.d.mts} +4 -4
- package/dist/{validate-kM8Pjuf7.d.mts.map → validate-DHxmpFJt.d.mts.map} +1 -1
- package/dist/validation-C-ZpN2GI.mjs +144 -0
- package/dist/validation-C-ZpN2GI.mjs.map +1 -0
- package/dist/version-Bbq8TCrz.mjs +7 -0
- package/dist/{version-BnTKdfam.mjs.map → version-Bbq8TCrz.mjs.map} +1 -1
- package/dist/zod-generator-CpwccCIv.mjs +132 -0
- package/dist/zod-generator-CpwccCIv.mjs.map +1 -0
- package/package.json +18 -5
- package/src/api/auth-storage.ts +37 -0
- package/src/api/error.ts +6 -0
- package/src/api/errors.ts +8 -0
- package/src/api/handlers/comments.ts +13 -0
- package/src/api/handlers/content.ts +122 -3
- package/src/api/handlers/index.ts +2 -0
- package/src/api/handlers/media.ts +8 -1
- package/src/api/handlers/menus.ts +160 -21
- package/src/api/handlers/redirects.ts +16 -3
- package/src/api/handlers/sections.ts +8 -1
- package/src/api/handlers/taxonomies.ts +128 -16
- package/src/api/handlers/validation.ts +212 -0
- package/src/api/openapi/document.ts +4 -1
- package/src/api/public-url.ts +6 -3
- package/src/api/route-utils.ts +14 -0
- package/src/api/schemas/common.ts +1 -1
- package/src/api/schemas/setup.ts +8 -0
- package/src/api/schemas/widgets.ts +12 -10
- package/src/api/setup-complete.ts +40 -0
- package/src/astro/integration/index.ts +13 -2
- package/src/astro/integration/routes.ts +28 -0
- package/src/astro/integration/runtime.ts +19 -1
- package/src/astro/integration/virtual-modules.ts +41 -0
- package/src/astro/integration/vite-config.ts +43 -12
- package/src/astro/middleware/auth.ts +21 -0
- package/src/astro/middleware.ts +18 -1
- package/src/astro/routes/PluginRegistry.tsx +10 -1
- package/src/astro/routes/api/auth/mode.ts +57 -0
- package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +23 -3
- package/src/astro/routes/api/auth/oauth/[provider].ts +10 -4
- package/src/astro/routes/api/content/[collection]/[id]/translations.ts +1 -1
- package/src/astro/routes/api/content/[collection]/index.ts +1 -9
- package/src/astro/routes/api/import/wordpress/media.ts +2 -7
- package/src/astro/routes/api/import/wordpress/prepare.ts +10 -0
- package/src/astro/routes/api/settings/email.ts +4 -9
- package/src/astro/routes/api/setup/admin.ts +8 -2
- package/src/astro/routes/api/setup/index.ts +2 -2
- package/src/astro/routes/api/setup/status.ts +3 -1
- package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +4 -1
- package/src/astro/routes/api/widget-areas/[name]/widgets.ts +4 -1
- package/src/astro/routes/api/widget-areas/[name].ts +4 -1
- package/src/astro/routes/api/widget-areas/index.ts +4 -1
- package/src/astro/types.ts +9 -0
- package/src/auth/mode.ts +15 -3
- package/src/auth/providers/github-admin.tsx +29 -0
- package/src/auth/providers/github.ts +31 -0
- package/src/auth/providers/google-admin.tsx +44 -0
- package/src/auth/providers/google.ts +31 -0
- package/src/auth/types.ts +114 -4
- package/src/cli/commands/bundle.ts +3 -1
- package/src/components/EmDashImage.astro +7 -6
- package/src/components/Gallery.astro +5 -3
- package/src/components/Image.astro +8 -3
- package/src/components/InlinePortableTextEditor.tsx +2 -1
- package/src/components/LiveSearch.astro +5 -14
- package/src/database/repositories/audit.ts +6 -8
- package/src/database/repositories/byline.ts +6 -8
- package/src/database/repositories/comment.ts +12 -16
- package/src/database/repositories/content.ts +40 -40
- package/src/database/repositories/index.ts +1 -1
- package/src/database/repositories/media.ts +10 -13
- package/src/database/repositories/plugin-storage.ts +4 -6
- package/src/database/repositories/redirect.ts +12 -16
- package/src/database/repositories/taxonomy.ts +14 -3
- package/src/database/repositories/types.ts +57 -8
- package/src/database/repositories/user.ts +6 -8
- package/src/emdash-runtime.ts +306 -90
- package/src/index.ts +5 -1
- package/src/loader.ts +6 -5
- package/src/mcp/server.ts +678 -105
- package/src/media/normalize.ts +1 -1
- package/src/media/url.ts +78 -0
- package/src/plugins/email-console.ts +10 -3
- package/src/plugins/hooks.ts +11 -0
- package/src/plugins/manifest-schema.ts +12 -0
- package/src/plugins/types.ts +23 -2
- package/src/query.ts +1 -1
- package/src/request-cache.ts +3 -0
- package/src/schema/registry.ts +41 -5
- package/src/search/fts-manager.ts +0 -2
- package/src/search/query.ts +111 -26
- package/src/search/types.ts +8 -1
- package/src/sections/index.ts +7 -9
- package/src/storage/s3.ts +12 -6
- package/src/virtual-modules.d.ts +21 -1
- package/src/widgets/index.ts +1 -1
- package/dist/apply-5uslYdUu.mjs.map +0 -1
- package/dist/byline-C4OVd8b3.mjs.map +0 -1
- package/dist/content-D7J5y73J.mjs.map +0 -1
- package/dist/error-CiYn9yDu.mjs.map +0 -1
- package/dist/index-De6_Xv3v.d.mts.map +0 -1
- package/dist/loader-DeiBJEMe.mjs.map +0 -1
- package/dist/manifest-schema-V30qsMft.mjs.map +0 -1
- package/dist/media-DqHVh136.mjs.map +0 -1
- package/dist/mode-CpNnGkPz.mjs.map +0 -1
- package/dist/redirect-CN0Rt9Ob.mjs.map +0 -1
- package/dist/registry-Ci3WxVAr.mjs.map +0 -1
- package/dist/request-cache-DiR961CV.mjs.map +0 -1
- package/dist/search-B0effn3j.mjs.map +0 -1
- package/dist/types-CMMN0pNg.mjs +0 -31
- package/dist/types-CMMN0pNg.mjs.map +0 -1
- package/dist/types-DgrIP0tF.d.mts.map +0 -1
- package/dist/version-BnTKdfam.mjs +0 -7
|
@@ -182,14 +182,12 @@ export class RedirectRepository {
|
|
|
182
182
|
|
|
183
183
|
if (opts.cursor) {
|
|
184
184
|
const decoded = decodeCursor(opts.cursor);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
eb.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
);
|
|
192
|
-
}
|
|
185
|
+
query = query.where((eb) =>
|
|
186
|
+
eb.or([
|
|
187
|
+
eb("created_at", "<", decoded.orderValue),
|
|
188
|
+
eb.and([eb("created_at", "=", decoded.orderValue), eb("id", "<", decoded.id)]),
|
|
189
|
+
]),
|
|
190
|
+
);
|
|
193
191
|
}
|
|
194
192
|
|
|
195
193
|
const rows = await query.execute();
|
|
@@ -509,14 +507,12 @@ export class RedirectRepository {
|
|
|
509
507
|
|
|
510
508
|
if (opts.cursor) {
|
|
511
509
|
const decoded = decodeCursor(opts.cursor);
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
eb.
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
);
|
|
519
|
-
}
|
|
510
|
+
query = query.where((eb) =>
|
|
511
|
+
eb.or([
|
|
512
|
+
eb("created_at", "<", decoded.orderValue),
|
|
513
|
+
eb.and([eb("created_at", "=", decoded.orderValue), eb("id", "<", decoded.id)]),
|
|
514
|
+
]),
|
|
515
|
+
);
|
|
520
516
|
}
|
|
521
517
|
|
|
522
518
|
const rows = await query.execute();
|
|
@@ -41,12 +41,15 @@ export class TaxonomyRepository {
|
|
|
41
41
|
async create(input: CreateTaxonomyInput): Promise<Taxonomy> {
|
|
42
42
|
const id = ulid();
|
|
43
43
|
|
|
44
|
+
// Empty-string parentId is coerced to null defensively. Higher layers
|
|
45
|
+
// also normalize this — see handleTermCreate / handleTermUpdate.
|
|
46
|
+
const parentId = input.parentId === undefined || input.parentId === "" ? null : input.parentId;
|
|
44
47
|
const row: TaxonomyTable = {
|
|
45
48
|
id,
|
|
46
49
|
name: input.name,
|
|
47
50
|
slug: input.slug,
|
|
48
51
|
label: input.label,
|
|
49
|
-
parent_id:
|
|
52
|
+
parent_id: parentId,
|
|
50
53
|
data: input.data ? JSON.stringify(input.data) : null,
|
|
51
54
|
};
|
|
52
55
|
|
|
@@ -90,11 +93,15 @@ export class TaxonomyRepository {
|
|
|
90
93
|
* Get all terms for a taxonomy (e.g., all categories)
|
|
91
94
|
*/
|
|
92
95
|
async findByName(name: string, options: { parentId?: string | null } = {}): Promise<Taxonomy[]> {
|
|
96
|
+
// `id asc` is a stable tiebreaker for terms that share a label.
|
|
97
|
+
// Without it the SQL ordering is implementation-defined when labels
|
|
98
|
+
// match, which breaks keyset pagination over `(label, id)`.
|
|
93
99
|
let query = this.db
|
|
94
100
|
.selectFrom("taxonomies")
|
|
95
101
|
.selectAll()
|
|
96
102
|
.where("name", "=", name)
|
|
97
|
-
.orderBy("label", "asc")
|
|
103
|
+
.orderBy("label", "asc")
|
|
104
|
+
.orderBy("id", "asc");
|
|
98
105
|
|
|
99
106
|
if (options.parentId !== undefined) {
|
|
100
107
|
if (options.parentId === null) {
|
|
@@ -117,6 +124,7 @@ export class TaxonomyRepository {
|
|
|
117
124
|
.selectAll()
|
|
118
125
|
.where("parent_id", "=", parentId)
|
|
119
126
|
.orderBy("label", "asc")
|
|
127
|
+
.orderBy("id", "asc")
|
|
120
128
|
.execute();
|
|
121
129
|
|
|
122
130
|
return rows.map((row) => this.rowToTaxonomy(row));
|
|
@@ -132,7 +140,10 @@ export class TaxonomyRepository {
|
|
|
132
140
|
const updates: Partial<TaxonomyTable> = {};
|
|
133
141
|
if (input.slug !== undefined) updates.slug = input.slug;
|
|
134
142
|
if (input.label !== undefined) updates.label = input.label;
|
|
135
|
-
if (input.parentId !== undefined)
|
|
143
|
+
if (input.parentId !== undefined) {
|
|
144
|
+
// Defense in depth: empty-string parentId means null (no parent).
|
|
145
|
+
updates.parent_id = input.parentId === "" ? null : input.parentId;
|
|
146
|
+
}
|
|
136
147
|
if (input.data !== undefined) updates.data = JSON.stringify(input.data);
|
|
137
148
|
|
|
138
149
|
if (Object.keys(updates).length > 0) {
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { encodeBase64, decodeBase64 } from "../../utils/base64.js";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Hard cap on cursor length. Cursors we issue are short JSON-in-base64
|
|
5
|
+
* blobs; a real cursor is well under 200 chars. This guards against
|
|
6
|
+
* malicious callers passing megabyte-sized strings to force the base64
|
|
7
|
+
* decoder to allocate (decodeBase64 is O(N) in input size). The MCP and
|
|
8
|
+
* REST schemas also clamp at 2048 — this 4096 cap is a defense-in-depth
|
|
9
|
+
* floor inside the repository helpers.
|
|
10
|
+
*/
|
|
11
|
+
const MAX_CURSOR_LENGTH = 4096;
|
|
12
|
+
|
|
3
13
|
export interface CreateContentInput {
|
|
4
14
|
type: string;
|
|
5
15
|
slug?: string | null;
|
|
@@ -87,17 +97,45 @@ export function encodeCursor(orderValue: string, id: string): string {
|
|
|
87
97
|
return encodeBase64(JSON.stringify({ orderValue, id }));
|
|
88
98
|
}
|
|
89
99
|
|
|
90
|
-
/**
|
|
91
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Thrown when a pagination cursor cannot be decoded.
|
|
102
|
+
*
|
|
103
|
+
* Repository callers should let this propagate; handler catch blocks
|
|
104
|
+
* map it to a structured `INVALID_CURSOR` error so client pagination
|
|
105
|
+
* bugs surface immediately rather than silently re-fetching the first
|
|
106
|
+
* page.
|
|
107
|
+
*/
|
|
108
|
+
export class InvalidCursorError extends Error {
|
|
109
|
+
constructor(cursor: string) {
|
|
110
|
+
const display = cursor.length > 50 ? `${cursor.slice(0, 47)}...` : cursor;
|
|
111
|
+
super(`Invalid pagination cursor: ${display}`);
|
|
112
|
+
this.name = "InvalidCursorError";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Decode a cursor to order value + id.
|
|
118
|
+
*
|
|
119
|
+
* Throws `InvalidCursorError` if the cursor is empty, not valid base64,
|
|
120
|
+
* not valid JSON, or doesn't contain string `orderValue` and `id` fields.
|
|
121
|
+
*/
|
|
122
|
+
export function decodeCursor(cursor: string): { orderValue: string; id: string } {
|
|
123
|
+
if (!cursor) throw new InvalidCursorError(cursor);
|
|
124
|
+
if (cursor.length > MAX_CURSOR_LENGTH) throw new InvalidCursorError(cursor);
|
|
125
|
+
let parsed: unknown;
|
|
92
126
|
try {
|
|
93
|
-
|
|
94
|
-
if (typeof parsed.orderValue === "string" && typeof parsed.id === "string") {
|
|
95
|
-
return parsed;
|
|
96
|
-
}
|
|
97
|
-
return null;
|
|
127
|
+
parsed = JSON.parse(decodeBase64(cursor));
|
|
98
128
|
} catch {
|
|
99
|
-
|
|
129
|
+
throw new InvalidCursorError(cursor);
|
|
130
|
+
}
|
|
131
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
132
|
+
throw new InvalidCursorError(cursor);
|
|
133
|
+
}
|
|
134
|
+
const candidate = parsed as { orderValue?: unknown; id?: unknown };
|
|
135
|
+
if (typeof candidate.orderValue !== "string" || typeof candidate.id !== "string") {
|
|
136
|
+
throw new InvalidCursorError(cursor);
|
|
100
137
|
}
|
|
138
|
+
return { orderValue: candidate.orderValue, id: candidate.id };
|
|
101
139
|
}
|
|
102
140
|
|
|
103
141
|
export interface ContentItem {
|
|
@@ -121,6 +159,17 @@ export interface ContentItem {
|
|
|
121
159
|
translationGroup: string | null;
|
|
122
160
|
/** SEO metadata — only populated for collections with `has_seo` enabled */
|
|
123
161
|
seo?: ContentSeo;
|
|
162
|
+
/**
|
|
163
|
+
* For collections that support `revisions`: when a draft revision exists,
|
|
164
|
+
* `data` reflects the unsaved draft and `liveData` carries the currently-
|
|
165
|
+
* published values. When no draft exists, `liveData` is undefined.
|
|
166
|
+
*
|
|
167
|
+
* Hydrated by `EmDashRuntime.hydrateDraftData()` — repositories themselves
|
|
168
|
+
* never set this field; it's purely a runtime-overlay concept that gives
|
|
169
|
+
* agents a clear picture of "draft vs. live" without re-fetching the
|
|
170
|
+
* revision history.
|
|
171
|
+
*/
|
|
172
|
+
liveData?: Record<string, unknown>;
|
|
124
173
|
}
|
|
125
174
|
|
|
126
175
|
export class EmDashValidationError extends Error {
|
|
@@ -123,14 +123,12 @@ export class UserRepository {
|
|
|
123
123
|
|
|
124
124
|
if (options.cursor) {
|
|
125
125
|
const decoded = decodeCursor(options.cursor);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
eb.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
);
|
|
133
|
-
}
|
|
126
|
+
query = query.where((eb) =>
|
|
127
|
+
eb.or([
|
|
128
|
+
eb("created_at", "<", decoded.orderValue),
|
|
129
|
+
eb.and([eb("created_at", "=", decoded.orderValue), eb("id", "<", decoded.id)]),
|
|
130
|
+
]),
|
|
131
|
+
);
|
|
134
132
|
}
|
|
135
133
|
|
|
136
134
|
const rows = await query.execute();
|