@realiizlabs/admin 0.11.2 → 0.13.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/Sidebar-CNuphww6.d.cts +60 -0
- package/dist/Sidebar-CNuphww6.d.ts +60 -0
- package/dist/auth/index.d.cts +5 -127
- package/dist/auth/index.d.ts +5 -127
- package/dist/auth-ui/index.js +124 -1
- package/dist/auth-ui/index.js.map +1 -1
- package/dist/forms/index.js +287 -1
- package/dist/forms/index.js.map +1 -1
- package/dist/forms-ui/index.js +355 -6
- package/dist/forms-ui/index.js.map +1 -1
- package/dist/git/index.cjs.map +1 -1
- package/dist/git/index.d.cts +3 -77
- package/dist/git/index.d.ts +3 -77
- package/dist/git/index.js.map +1 -1
- package/dist/members-nhbRSYyr.d.cts +106 -0
- package/dist/members-nhbRSYyr.d.ts +106 -0
- package/dist/roles-BjWuj2_v.d.cts +25 -0
- package/dist/roles-Dnwj-DAm.d.ts +25 -0
- package/dist/shell/index.d.cts +3 -58
- package/dist/shell/index.d.ts +3 -58
- package/dist/studio/index.cjs +1150 -0
- package/dist/studio/index.cjs.map +1 -0
- package/dist/studio/index.d.cts +325 -0
- package/dist/studio/index.d.ts +325 -0
- package/dist/studio/index.js +1131 -0
- package/dist/studio/index.js.map +1 -0
- package/dist/studio-ui/index.cjs +3931 -0
- package/dist/studio-ui/index.cjs.map +1 -0
- package/dist/studio-ui/index.d.cts +375 -0
- package/dist/studio-ui/index.d.ts +375 -0
- package/dist/studio-ui/index.js +3900 -0
- package/dist/studio-ui/index.js.map +1 -0
- package/dist/types-BP5G1myE.d.cts +78 -0
- package/dist/types-BP5G1myE.d.ts +78 -0
- package/package.json +20 -2
- package/dist/chunk-2GSYBARR.js +0 -126
- package/dist/chunk-2GSYBARR.js.map +0 -1
- package/dist/chunk-JLR5K6RP.js +0 -289
- package/dist/chunk-JLR5K6RP.js.map +0 -1
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { a as IdentityUser, f as SiteRole, C as CookieStore, d as SiteMember, I as IdentityClient, c as SiteBranding } from '../members-nhbRSYyr.cjs';
|
|
2
|
+
import { a as PullRequestStatus } from '../types-BP5G1myE.cjs';
|
|
3
|
+
import { C as ContentTypeEntry } from '../types-D46UjOfv.cjs';
|
|
4
|
+
import * as zod from 'zod';
|
|
5
|
+
import * as zod_v4_core from 'zod/v4/core';
|
|
6
|
+
import { R as RequireOptions } from '../roles-BjWuj2_v.cjs';
|
|
7
|
+
import '@supabase/supabase-js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Server-side checks on the pictures a save carries. Pure.
|
|
11
|
+
*
|
|
12
|
+
* The browser already shrank them, but anyone can POST to a server action, so
|
|
13
|
+
* the path, type and size are re-checked before anything is committed: only
|
|
14
|
+
* WebP under the type's public folder, ≤ 800 KB each, ≤ 3.5 MB per save.
|
|
15
|
+
* Frontmatter paths are URL paths (/blog/x.webp); the repo file lives under public/.
|
|
16
|
+
*/
|
|
17
|
+
interface ImagePayload {
|
|
18
|
+
/** URL path as written in the frontmatter or body, e.g. /blog/my-post.webp. */
|
|
19
|
+
path: string;
|
|
20
|
+
/** Base64 of the WebP bytes. */
|
|
21
|
+
base64: string;
|
|
22
|
+
}
|
|
23
|
+
interface ImageLimits {
|
|
24
|
+
maxEach: number;
|
|
25
|
+
maxTotal: number;
|
|
26
|
+
/** URL prefix pictures must live under. */
|
|
27
|
+
prefix: string;
|
|
28
|
+
}
|
|
29
|
+
declare const IMAGE_LIMITS: ImageLimits;
|
|
30
|
+
declare const IMAGE_ERRORS: {
|
|
31
|
+
readonly path: "That image path isn’t allowed.";
|
|
32
|
+
readonly size: "That picture is too large to send — try a smaller one.";
|
|
33
|
+
};
|
|
34
|
+
type ValidatedImages = {
|
|
35
|
+
ok: true;
|
|
36
|
+
files: {
|
|
37
|
+
path: string;
|
|
38
|
+
content: string;
|
|
39
|
+
encoding: "base64";
|
|
40
|
+
}[];
|
|
41
|
+
} | {
|
|
42
|
+
ok: false;
|
|
43
|
+
error: string;
|
|
44
|
+
};
|
|
45
|
+
/** Bytes a base64 string decodes to, without decoding it. */
|
|
46
|
+
declare function decodedSize(b64: string): number;
|
|
47
|
+
declare function validateImages(images: ImagePayload[], limits?: ImageLimits): ValidatedImages;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The contract between a site and its Studio. Everything a site knows and the
|
|
51
|
+
* package cannot — env values, cookies, the request's origin, its registry —
|
|
52
|
+
* arrives here, lazily, so `next build` with no env set still succeeds.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
interface RepoConfig {
|
|
56
|
+
owner: string;
|
|
57
|
+
repo: string;
|
|
58
|
+
token: string;
|
|
59
|
+
}
|
|
60
|
+
interface IdentityConfig {
|
|
61
|
+
url: string;
|
|
62
|
+
anonKey: string;
|
|
63
|
+
}
|
|
64
|
+
interface ServiceConfig {
|
|
65
|
+
url: string;
|
|
66
|
+
serviceRoleKey: string;
|
|
67
|
+
}
|
|
68
|
+
/** Read per request, never at import time (BUILD-ENV law lives in the site's env.ts). */
|
|
69
|
+
interface StudioEnv {
|
|
70
|
+
siteId: string;
|
|
71
|
+
repo: RepoConfig;
|
|
72
|
+
identity: IdentityConfig;
|
|
73
|
+
/** Optional: without it the Team tab explains that invites aren't set up. */
|
|
74
|
+
service: ServiceConfig | null;
|
|
75
|
+
}
|
|
76
|
+
interface StudioConfig {
|
|
77
|
+
/** The site's registry, keyed by type id. */
|
|
78
|
+
contentTypes: Record<string, ContentTypeEntry>;
|
|
79
|
+
/** Public page for an item, per type; a type with no page returns null. */
|
|
80
|
+
publicUrls?: Record<string, (slug: string) => string | null>;
|
|
81
|
+
/** Called per request. Throw a clear message when something is missing. */
|
|
82
|
+
env: () => StudioEnv;
|
|
83
|
+
/** True when every variable is present — the layout shows a setup page otherwise. */
|
|
84
|
+
isConfigured: () => boolean;
|
|
85
|
+
/** This request's cookie store (next/headers `cookies()` in a site). */
|
|
86
|
+
cookies: () => Promise<CookieStore>;
|
|
87
|
+
/** proto://host of this request — where auth links come back to. */
|
|
88
|
+
baseUrl: () => Promise<string>;
|
|
89
|
+
/** Next's revalidatePath, passed in so the package never imports next/cache. */
|
|
90
|
+
revalidate?: (path: string, type?: "layout" | "page") => void;
|
|
91
|
+
/** Shown in Studio's own copy ("Ask Realiiz" → "Ask {supportName}"). */
|
|
92
|
+
supportName?: string;
|
|
93
|
+
siteName: string;
|
|
94
|
+
/** IANA zone used when an event names none. */
|
|
95
|
+
timezone?: string;
|
|
96
|
+
}
|
|
97
|
+
type ActionResult = {
|
|
98
|
+
ok: true;
|
|
99
|
+
message?: string;
|
|
100
|
+
} | {
|
|
101
|
+
ok: false;
|
|
102
|
+
error: string;
|
|
103
|
+
};
|
|
104
|
+
interface Session {
|
|
105
|
+
user: IdentityUser;
|
|
106
|
+
role: SiteRole;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface Located {
|
|
110
|
+
typeId: string;
|
|
111
|
+
slug: string;
|
|
112
|
+
entry: ContentTypeEntry;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The dashboard's own in-flight changes: open PRs on `admin/*` branches, keyed
|
|
117
|
+
* by the file they change. The branch name — `admin/{typeId}/{fileBase}-{stamp}`
|
|
118
|
+
* — is the index, so no extra call per PR is needed. One open change per file;
|
|
119
|
+
* if two ever exist the newest wins here and the older is left alone on GitHub.
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
type PendingState = "checking" | "ready" | "failed";
|
|
123
|
+
interface PendingChange {
|
|
124
|
+
number: number;
|
|
125
|
+
branch: string;
|
|
126
|
+
title: string;
|
|
127
|
+
html_url: string;
|
|
128
|
+
path: string;
|
|
129
|
+
typeId: string;
|
|
130
|
+
state: PendingState;
|
|
131
|
+
failingCheck: string | null;
|
|
132
|
+
/** The change removes the item rather than editing it. */
|
|
133
|
+
removal: boolean;
|
|
134
|
+
}
|
|
135
|
+
declare const pendingLabel: Record<PendingState, string>;
|
|
136
|
+
declare function pendingText(p: PendingChange): string;
|
|
137
|
+
|
|
138
|
+
interface ListItem {
|
|
139
|
+
slug: string;
|
|
140
|
+
path: string;
|
|
141
|
+
title: string;
|
|
142
|
+
/** publishedAt, falling back to date — ISO string, or null when the file has neither. */
|
|
143
|
+
date: string | null;
|
|
144
|
+
/** True when the frontmatter did not parse cleanly — still listed, still openable. */
|
|
145
|
+
needsAttention: boolean;
|
|
146
|
+
pending: PendingChange | null;
|
|
147
|
+
/** The file exists only in a pending change — nothing on the live site yet. */
|
|
148
|
+
isNew: boolean;
|
|
149
|
+
}
|
|
150
|
+
interface PrSummary {
|
|
151
|
+
number: number;
|
|
152
|
+
title: string;
|
|
153
|
+
html_url: string;
|
|
154
|
+
merged: boolean;
|
|
155
|
+
state: "open" | "closed";
|
|
156
|
+
/** Parsed from the PR body the publish action wrote, when present. */
|
|
157
|
+
postPath: string | null;
|
|
158
|
+
branch: string;
|
|
159
|
+
removal: boolean;
|
|
160
|
+
/** The commit that landed on main when this was published; null until then. */
|
|
161
|
+
mergeCommitSha: string | null;
|
|
162
|
+
}
|
|
163
|
+
/** Thrown when no file in the folder maps to the slug — the page turns it into a 404. */
|
|
164
|
+
declare class NotHere extends Error {
|
|
165
|
+
constructor(slug: string);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Publish and remove: validated submission → one commit on a new branch → PR.
|
|
170
|
+
* The branch is cut from `main` as it is at this moment and named with a stamp
|
|
171
|
+
* so two edits of one file never collide. Nothing goes to main directly (PR Law).
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
interface PublishInput {
|
|
175
|
+
type: string;
|
|
176
|
+
frontmatter: Record<string, unknown>;
|
|
177
|
+
body: string;
|
|
178
|
+
/** An open change for this file: save onto ITS branch so the same PR picks up the edit. */
|
|
179
|
+
existing?: {
|
|
180
|
+
number: number;
|
|
181
|
+
branch: string;
|
|
182
|
+
} | null;
|
|
183
|
+
/** Pictures chosen in this session, already shrunk in the browser; committed with the MDX. */
|
|
184
|
+
images?: ImagePayload[];
|
|
185
|
+
}
|
|
186
|
+
type PublishResult = {
|
|
187
|
+
ok: false;
|
|
188
|
+
error: string;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/** The Publish button (merge when green, never otherwise) and Discard (close + delete branch). */
|
|
192
|
+
|
|
193
|
+
type MergeOutcome = {
|
|
194
|
+
state: "merged";
|
|
195
|
+
sha: string;
|
|
196
|
+
} | {
|
|
197
|
+
state: "pending";
|
|
198
|
+
} | {
|
|
199
|
+
state: "stale";
|
|
200
|
+
} | {
|
|
201
|
+
state: "red";
|
|
202
|
+
failingCheck: string | null;
|
|
203
|
+
} | {
|
|
204
|
+
state: "error";
|
|
205
|
+
message: string;
|
|
206
|
+
};
|
|
207
|
+
type DiscardOutcome = {
|
|
208
|
+
state: "discarded";
|
|
209
|
+
} | {
|
|
210
|
+
state: "already-published";
|
|
211
|
+
} | {
|
|
212
|
+
state: "error";
|
|
213
|
+
message: string;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Team actions. Reading the roster uses the signed-in user's client (the
|
|
218
|
+
* site_members() function refuses non-owners). Writes need the service role,
|
|
219
|
+
* which the auth helpers use ONLY after re-checking the actor's role in code.
|
|
220
|
+
*/
|
|
221
|
+
|
|
222
|
+
type TeamState = {
|
|
223
|
+
ok: true;
|
|
224
|
+
members: SiteMember[];
|
|
225
|
+
canInvite: boolean;
|
|
226
|
+
} | {
|
|
227
|
+
ok: false;
|
|
228
|
+
error: string;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
declare function createStudio(config: StudioConfig): {
|
|
232
|
+
config: StudioConfig;
|
|
233
|
+
isConfigured: () => boolean;
|
|
234
|
+
entry: (typeId: string) => ContentTypeEntry | null;
|
|
235
|
+
entries: ContentTypeEntry<zod.ZodType<unknown, unknown, zod_v4_core.$ZodTypeInternals<unknown, unknown>>>[];
|
|
236
|
+
requireAdmin: (opts?: RequireOptions) => Promise<Session>;
|
|
237
|
+
identityClient: () => Promise<IdentityClient>;
|
|
238
|
+
readers: {
|
|
239
|
+
locate: (path: string) => Located | null;
|
|
240
|
+
publicUrlFor: (typeId: string, slug: string) => string | null;
|
|
241
|
+
publicDirFor: (typeId: string) => string;
|
|
242
|
+
listPendingChanges: () => Promise<Map<string, PendingChange>>;
|
|
243
|
+
pendingFor: (path: string) => Promise<PendingChange | null>;
|
|
244
|
+
pathOf: (typeId: string, slug: string) => Promise<string | null>;
|
|
245
|
+
listItems: (typeId: string) => Promise<ListItem[]>;
|
|
246
|
+
countItems: (typeId: string) => Promise<number>;
|
|
247
|
+
readItem: (typeId: string, slug: string, opts?: {
|
|
248
|
+
ref?: string;
|
|
249
|
+
}) => Promise<{
|
|
250
|
+
frontmatter: Record<string, unknown>;
|
|
251
|
+
body: string;
|
|
252
|
+
sha: string;
|
|
253
|
+
path: string;
|
|
254
|
+
}>;
|
|
255
|
+
lastAuthor: (typeId: string) => Promise<string | null>;
|
|
256
|
+
getPullRequest: (number: number) => Promise<PrSummary | null>;
|
|
257
|
+
prStatus: (number: number) => Promise<PullRequestStatus>;
|
|
258
|
+
};
|
|
259
|
+
actions: {
|
|
260
|
+
loadTeam: () => Promise<TeamState>;
|
|
261
|
+
invite: (input: {
|
|
262
|
+
email: string;
|
|
263
|
+
role: "owner" | "editor";
|
|
264
|
+
}) => Promise<ActionResult>;
|
|
265
|
+
changeRole: (input: {
|
|
266
|
+
userId: string;
|
|
267
|
+
role: "owner" | "editor";
|
|
268
|
+
}) => Promise<ActionResult>;
|
|
269
|
+
resend: (input: {
|
|
270
|
+
email: string;
|
|
271
|
+
}) => Promise<ActionResult>;
|
|
272
|
+
remove: (input: {
|
|
273
|
+
userId: string;
|
|
274
|
+
}) => Promise<ActionResult>;
|
|
275
|
+
saveProfile: (input: {
|
|
276
|
+
name: string;
|
|
277
|
+
displayName: string;
|
|
278
|
+
}) => Promise<ActionResult>;
|
|
279
|
+
saveAvatar: (input: {
|
|
280
|
+
base64: string;
|
|
281
|
+
} | null) => Promise<ActionResult>;
|
|
282
|
+
changePassword: (input: {
|
|
283
|
+
password: string;
|
|
284
|
+
confirm: string;
|
|
285
|
+
}) => Promise<ActionResult>;
|
|
286
|
+
loadBranding: () => Promise<SiteBranding | null>;
|
|
287
|
+
saveBranding: (input: {
|
|
288
|
+
name: string;
|
|
289
|
+
businessEmail: string;
|
|
290
|
+
businessPhone: string;
|
|
291
|
+
}) => Promise<ActionResult>;
|
|
292
|
+
saveBrandingPicture: (input: {
|
|
293
|
+
kind: "logo" | "favicon";
|
|
294
|
+
base64: string | null;
|
|
295
|
+
}) => Promise<ActionResult>;
|
|
296
|
+
loadMe: () => Promise<{
|
|
297
|
+
name: string | null;
|
|
298
|
+
displayName: string | null;
|
|
299
|
+
avatarUrl: string | null;
|
|
300
|
+
email: string;
|
|
301
|
+
role: SiteRole;
|
|
302
|
+
}>;
|
|
303
|
+
previewBody: (markdown: string) => Promise<{
|
|
304
|
+
html: string;
|
|
305
|
+
} | {
|
|
306
|
+
error: string;
|
|
307
|
+
}>;
|
|
308
|
+
mergeContentPr: (number: number) => Promise<MergeOutcome>;
|
|
309
|
+
discardChange: (number: number, branch: string) => Promise<DiscardOutcome>;
|
|
310
|
+
publishContent: (input: PublishInput) => Promise<PublishResult | never>;
|
|
311
|
+
removeContent: (input: {
|
|
312
|
+
type: string;
|
|
313
|
+
slug: string;
|
|
314
|
+
}) => Promise<PublishResult | never>;
|
|
315
|
+
};
|
|
316
|
+
authHandlers: {
|
|
317
|
+
callback: (request: Request) => Promise<Response>;
|
|
318
|
+
signout: (request: Request) => Promise<Response>;
|
|
319
|
+
};
|
|
320
|
+
};
|
|
321
|
+
type Studio = ReturnType<typeof createStudio>;
|
|
322
|
+
type StudioActions = Studio["actions"];
|
|
323
|
+
type StudioReaders = Studio["readers"];
|
|
324
|
+
|
|
325
|
+
export { type ActionResult, type DiscardOutcome, IMAGE_ERRORS, IMAGE_LIMITS, type IdentityConfig, type ImageLimits, type ImagePayload, type ListItem, type Located, type MergeOutcome, NotHere, type PendingChange, type PendingState, type PrSummary, type PublishInput, type PublishResult, type RepoConfig, type ServiceConfig, type Session, type Studio, type StudioActions, type StudioConfig, type StudioEnv, type StudioReaders, type TeamState, type ValidatedImages, createStudio, decodedSize, pendingLabel, pendingText, validateImages };
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { a as IdentityUser, f as SiteRole, C as CookieStore, d as SiteMember, I as IdentityClient, c as SiteBranding } from '../members-nhbRSYyr.js';
|
|
2
|
+
import { a as PullRequestStatus } from '../types-BP5G1myE.js';
|
|
3
|
+
import { C as ContentTypeEntry } from '../types-D46UjOfv.js';
|
|
4
|
+
import * as zod from 'zod';
|
|
5
|
+
import * as zod_v4_core from 'zod/v4/core';
|
|
6
|
+
import { R as RequireOptions } from '../roles-Dnwj-DAm.js';
|
|
7
|
+
import '@supabase/supabase-js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Server-side checks on the pictures a save carries. Pure.
|
|
11
|
+
*
|
|
12
|
+
* The browser already shrank them, but anyone can POST to a server action, so
|
|
13
|
+
* the path, type and size are re-checked before anything is committed: only
|
|
14
|
+
* WebP under the type's public folder, ≤ 800 KB each, ≤ 3.5 MB per save.
|
|
15
|
+
* Frontmatter paths are URL paths (/blog/x.webp); the repo file lives under public/.
|
|
16
|
+
*/
|
|
17
|
+
interface ImagePayload {
|
|
18
|
+
/** URL path as written in the frontmatter or body, e.g. /blog/my-post.webp. */
|
|
19
|
+
path: string;
|
|
20
|
+
/** Base64 of the WebP bytes. */
|
|
21
|
+
base64: string;
|
|
22
|
+
}
|
|
23
|
+
interface ImageLimits {
|
|
24
|
+
maxEach: number;
|
|
25
|
+
maxTotal: number;
|
|
26
|
+
/** URL prefix pictures must live under. */
|
|
27
|
+
prefix: string;
|
|
28
|
+
}
|
|
29
|
+
declare const IMAGE_LIMITS: ImageLimits;
|
|
30
|
+
declare const IMAGE_ERRORS: {
|
|
31
|
+
readonly path: "That image path isn’t allowed.";
|
|
32
|
+
readonly size: "That picture is too large to send — try a smaller one.";
|
|
33
|
+
};
|
|
34
|
+
type ValidatedImages = {
|
|
35
|
+
ok: true;
|
|
36
|
+
files: {
|
|
37
|
+
path: string;
|
|
38
|
+
content: string;
|
|
39
|
+
encoding: "base64";
|
|
40
|
+
}[];
|
|
41
|
+
} | {
|
|
42
|
+
ok: false;
|
|
43
|
+
error: string;
|
|
44
|
+
};
|
|
45
|
+
/** Bytes a base64 string decodes to, without decoding it. */
|
|
46
|
+
declare function decodedSize(b64: string): number;
|
|
47
|
+
declare function validateImages(images: ImagePayload[], limits?: ImageLimits): ValidatedImages;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The contract between a site and its Studio. Everything a site knows and the
|
|
51
|
+
* package cannot — env values, cookies, the request's origin, its registry —
|
|
52
|
+
* arrives here, lazily, so `next build` with no env set still succeeds.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
interface RepoConfig {
|
|
56
|
+
owner: string;
|
|
57
|
+
repo: string;
|
|
58
|
+
token: string;
|
|
59
|
+
}
|
|
60
|
+
interface IdentityConfig {
|
|
61
|
+
url: string;
|
|
62
|
+
anonKey: string;
|
|
63
|
+
}
|
|
64
|
+
interface ServiceConfig {
|
|
65
|
+
url: string;
|
|
66
|
+
serviceRoleKey: string;
|
|
67
|
+
}
|
|
68
|
+
/** Read per request, never at import time (BUILD-ENV law lives in the site's env.ts). */
|
|
69
|
+
interface StudioEnv {
|
|
70
|
+
siteId: string;
|
|
71
|
+
repo: RepoConfig;
|
|
72
|
+
identity: IdentityConfig;
|
|
73
|
+
/** Optional: without it the Team tab explains that invites aren't set up. */
|
|
74
|
+
service: ServiceConfig | null;
|
|
75
|
+
}
|
|
76
|
+
interface StudioConfig {
|
|
77
|
+
/** The site's registry, keyed by type id. */
|
|
78
|
+
contentTypes: Record<string, ContentTypeEntry>;
|
|
79
|
+
/** Public page for an item, per type; a type with no page returns null. */
|
|
80
|
+
publicUrls?: Record<string, (slug: string) => string | null>;
|
|
81
|
+
/** Called per request. Throw a clear message when something is missing. */
|
|
82
|
+
env: () => StudioEnv;
|
|
83
|
+
/** True when every variable is present — the layout shows a setup page otherwise. */
|
|
84
|
+
isConfigured: () => boolean;
|
|
85
|
+
/** This request's cookie store (next/headers `cookies()` in a site). */
|
|
86
|
+
cookies: () => Promise<CookieStore>;
|
|
87
|
+
/** proto://host of this request — where auth links come back to. */
|
|
88
|
+
baseUrl: () => Promise<string>;
|
|
89
|
+
/** Next's revalidatePath, passed in so the package never imports next/cache. */
|
|
90
|
+
revalidate?: (path: string, type?: "layout" | "page") => void;
|
|
91
|
+
/** Shown in Studio's own copy ("Ask Realiiz" → "Ask {supportName}"). */
|
|
92
|
+
supportName?: string;
|
|
93
|
+
siteName: string;
|
|
94
|
+
/** IANA zone used when an event names none. */
|
|
95
|
+
timezone?: string;
|
|
96
|
+
}
|
|
97
|
+
type ActionResult = {
|
|
98
|
+
ok: true;
|
|
99
|
+
message?: string;
|
|
100
|
+
} | {
|
|
101
|
+
ok: false;
|
|
102
|
+
error: string;
|
|
103
|
+
};
|
|
104
|
+
interface Session {
|
|
105
|
+
user: IdentityUser;
|
|
106
|
+
role: SiteRole;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface Located {
|
|
110
|
+
typeId: string;
|
|
111
|
+
slug: string;
|
|
112
|
+
entry: ContentTypeEntry;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The dashboard's own in-flight changes: open PRs on `admin/*` branches, keyed
|
|
117
|
+
* by the file they change. The branch name — `admin/{typeId}/{fileBase}-{stamp}`
|
|
118
|
+
* — is the index, so no extra call per PR is needed. One open change per file;
|
|
119
|
+
* if two ever exist the newest wins here and the older is left alone on GitHub.
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
type PendingState = "checking" | "ready" | "failed";
|
|
123
|
+
interface PendingChange {
|
|
124
|
+
number: number;
|
|
125
|
+
branch: string;
|
|
126
|
+
title: string;
|
|
127
|
+
html_url: string;
|
|
128
|
+
path: string;
|
|
129
|
+
typeId: string;
|
|
130
|
+
state: PendingState;
|
|
131
|
+
failingCheck: string | null;
|
|
132
|
+
/** The change removes the item rather than editing it. */
|
|
133
|
+
removal: boolean;
|
|
134
|
+
}
|
|
135
|
+
declare const pendingLabel: Record<PendingState, string>;
|
|
136
|
+
declare function pendingText(p: PendingChange): string;
|
|
137
|
+
|
|
138
|
+
interface ListItem {
|
|
139
|
+
slug: string;
|
|
140
|
+
path: string;
|
|
141
|
+
title: string;
|
|
142
|
+
/** publishedAt, falling back to date — ISO string, or null when the file has neither. */
|
|
143
|
+
date: string | null;
|
|
144
|
+
/** True when the frontmatter did not parse cleanly — still listed, still openable. */
|
|
145
|
+
needsAttention: boolean;
|
|
146
|
+
pending: PendingChange | null;
|
|
147
|
+
/** The file exists only in a pending change — nothing on the live site yet. */
|
|
148
|
+
isNew: boolean;
|
|
149
|
+
}
|
|
150
|
+
interface PrSummary {
|
|
151
|
+
number: number;
|
|
152
|
+
title: string;
|
|
153
|
+
html_url: string;
|
|
154
|
+
merged: boolean;
|
|
155
|
+
state: "open" | "closed";
|
|
156
|
+
/** Parsed from the PR body the publish action wrote, when present. */
|
|
157
|
+
postPath: string | null;
|
|
158
|
+
branch: string;
|
|
159
|
+
removal: boolean;
|
|
160
|
+
/** The commit that landed on main when this was published; null until then. */
|
|
161
|
+
mergeCommitSha: string | null;
|
|
162
|
+
}
|
|
163
|
+
/** Thrown when no file in the folder maps to the slug — the page turns it into a 404. */
|
|
164
|
+
declare class NotHere extends Error {
|
|
165
|
+
constructor(slug: string);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Publish and remove: validated submission → one commit on a new branch → PR.
|
|
170
|
+
* The branch is cut from `main` as it is at this moment and named with a stamp
|
|
171
|
+
* so two edits of one file never collide. Nothing goes to main directly (PR Law).
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
interface PublishInput {
|
|
175
|
+
type: string;
|
|
176
|
+
frontmatter: Record<string, unknown>;
|
|
177
|
+
body: string;
|
|
178
|
+
/** An open change for this file: save onto ITS branch so the same PR picks up the edit. */
|
|
179
|
+
existing?: {
|
|
180
|
+
number: number;
|
|
181
|
+
branch: string;
|
|
182
|
+
} | null;
|
|
183
|
+
/** Pictures chosen in this session, already shrunk in the browser; committed with the MDX. */
|
|
184
|
+
images?: ImagePayload[];
|
|
185
|
+
}
|
|
186
|
+
type PublishResult = {
|
|
187
|
+
ok: false;
|
|
188
|
+
error: string;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/** The Publish button (merge when green, never otherwise) and Discard (close + delete branch). */
|
|
192
|
+
|
|
193
|
+
type MergeOutcome = {
|
|
194
|
+
state: "merged";
|
|
195
|
+
sha: string;
|
|
196
|
+
} | {
|
|
197
|
+
state: "pending";
|
|
198
|
+
} | {
|
|
199
|
+
state: "stale";
|
|
200
|
+
} | {
|
|
201
|
+
state: "red";
|
|
202
|
+
failingCheck: string | null;
|
|
203
|
+
} | {
|
|
204
|
+
state: "error";
|
|
205
|
+
message: string;
|
|
206
|
+
};
|
|
207
|
+
type DiscardOutcome = {
|
|
208
|
+
state: "discarded";
|
|
209
|
+
} | {
|
|
210
|
+
state: "already-published";
|
|
211
|
+
} | {
|
|
212
|
+
state: "error";
|
|
213
|
+
message: string;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Team actions. Reading the roster uses the signed-in user's client (the
|
|
218
|
+
* site_members() function refuses non-owners). Writes need the service role,
|
|
219
|
+
* which the auth helpers use ONLY after re-checking the actor's role in code.
|
|
220
|
+
*/
|
|
221
|
+
|
|
222
|
+
type TeamState = {
|
|
223
|
+
ok: true;
|
|
224
|
+
members: SiteMember[];
|
|
225
|
+
canInvite: boolean;
|
|
226
|
+
} | {
|
|
227
|
+
ok: false;
|
|
228
|
+
error: string;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
declare function createStudio(config: StudioConfig): {
|
|
232
|
+
config: StudioConfig;
|
|
233
|
+
isConfigured: () => boolean;
|
|
234
|
+
entry: (typeId: string) => ContentTypeEntry | null;
|
|
235
|
+
entries: ContentTypeEntry<zod.ZodType<unknown, unknown, zod_v4_core.$ZodTypeInternals<unknown, unknown>>>[];
|
|
236
|
+
requireAdmin: (opts?: RequireOptions) => Promise<Session>;
|
|
237
|
+
identityClient: () => Promise<IdentityClient>;
|
|
238
|
+
readers: {
|
|
239
|
+
locate: (path: string) => Located | null;
|
|
240
|
+
publicUrlFor: (typeId: string, slug: string) => string | null;
|
|
241
|
+
publicDirFor: (typeId: string) => string;
|
|
242
|
+
listPendingChanges: () => Promise<Map<string, PendingChange>>;
|
|
243
|
+
pendingFor: (path: string) => Promise<PendingChange | null>;
|
|
244
|
+
pathOf: (typeId: string, slug: string) => Promise<string | null>;
|
|
245
|
+
listItems: (typeId: string) => Promise<ListItem[]>;
|
|
246
|
+
countItems: (typeId: string) => Promise<number>;
|
|
247
|
+
readItem: (typeId: string, slug: string, opts?: {
|
|
248
|
+
ref?: string;
|
|
249
|
+
}) => Promise<{
|
|
250
|
+
frontmatter: Record<string, unknown>;
|
|
251
|
+
body: string;
|
|
252
|
+
sha: string;
|
|
253
|
+
path: string;
|
|
254
|
+
}>;
|
|
255
|
+
lastAuthor: (typeId: string) => Promise<string | null>;
|
|
256
|
+
getPullRequest: (number: number) => Promise<PrSummary | null>;
|
|
257
|
+
prStatus: (number: number) => Promise<PullRequestStatus>;
|
|
258
|
+
};
|
|
259
|
+
actions: {
|
|
260
|
+
loadTeam: () => Promise<TeamState>;
|
|
261
|
+
invite: (input: {
|
|
262
|
+
email: string;
|
|
263
|
+
role: "owner" | "editor";
|
|
264
|
+
}) => Promise<ActionResult>;
|
|
265
|
+
changeRole: (input: {
|
|
266
|
+
userId: string;
|
|
267
|
+
role: "owner" | "editor";
|
|
268
|
+
}) => Promise<ActionResult>;
|
|
269
|
+
resend: (input: {
|
|
270
|
+
email: string;
|
|
271
|
+
}) => Promise<ActionResult>;
|
|
272
|
+
remove: (input: {
|
|
273
|
+
userId: string;
|
|
274
|
+
}) => Promise<ActionResult>;
|
|
275
|
+
saveProfile: (input: {
|
|
276
|
+
name: string;
|
|
277
|
+
displayName: string;
|
|
278
|
+
}) => Promise<ActionResult>;
|
|
279
|
+
saveAvatar: (input: {
|
|
280
|
+
base64: string;
|
|
281
|
+
} | null) => Promise<ActionResult>;
|
|
282
|
+
changePassword: (input: {
|
|
283
|
+
password: string;
|
|
284
|
+
confirm: string;
|
|
285
|
+
}) => Promise<ActionResult>;
|
|
286
|
+
loadBranding: () => Promise<SiteBranding | null>;
|
|
287
|
+
saveBranding: (input: {
|
|
288
|
+
name: string;
|
|
289
|
+
businessEmail: string;
|
|
290
|
+
businessPhone: string;
|
|
291
|
+
}) => Promise<ActionResult>;
|
|
292
|
+
saveBrandingPicture: (input: {
|
|
293
|
+
kind: "logo" | "favicon";
|
|
294
|
+
base64: string | null;
|
|
295
|
+
}) => Promise<ActionResult>;
|
|
296
|
+
loadMe: () => Promise<{
|
|
297
|
+
name: string | null;
|
|
298
|
+
displayName: string | null;
|
|
299
|
+
avatarUrl: string | null;
|
|
300
|
+
email: string;
|
|
301
|
+
role: SiteRole;
|
|
302
|
+
}>;
|
|
303
|
+
previewBody: (markdown: string) => Promise<{
|
|
304
|
+
html: string;
|
|
305
|
+
} | {
|
|
306
|
+
error: string;
|
|
307
|
+
}>;
|
|
308
|
+
mergeContentPr: (number: number) => Promise<MergeOutcome>;
|
|
309
|
+
discardChange: (number: number, branch: string) => Promise<DiscardOutcome>;
|
|
310
|
+
publishContent: (input: PublishInput) => Promise<PublishResult | never>;
|
|
311
|
+
removeContent: (input: {
|
|
312
|
+
type: string;
|
|
313
|
+
slug: string;
|
|
314
|
+
}) => Promise<PublishResult | never>;
|
|
315
|
+
};
|
|
316
|
+
authHandlers: {
|
|
317
|
+
callback: (request: Request) => Promise<Response>;
|
|
318
|
+
signout: (request: Request) => Promise<Response>;
|
|
319
|
+
};
|
|
320
|
+
};
|
|
321
|
+
type Studio = ReturnType<typeof createStudio>;
|
|
322
|
+
type StudioActions = Studio["actions"];
|
|
323
|
+
type StudioReaders = Studio["readers"];
|
|
324
|
+
|
|
325
|
+
export { type ActionResult, type DiscardOutcome, IMAGE_ERRORS, IMAGE_LIMITS, type IdentityConfig, type ImageLimits, type ImagePayload, type ListItem, type Located, type MergeOutcome, NotHere, type PendingChange, type PendingState, type PrSummary, type PublishInput, type PublishResult, type RepoConfig, type ServiceConfig, type Session, type Studio, type StudioActions, type StudioConfig, type StudioEnv, type StudioReaders, type TeamState, type ValidatedImages, createStudio, decodedSize, pendingLabel, pendingText, validateImages };
|