@realiizlabs/admin 0.14.1 → 0.15.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.
@@ -5,6 +5,7 @@ import { a as PullRequestStatus } from '../types-BP5G1myE.cjs';
5
5
  import { C as ContentTypeEntry } from '../types-DeHdasdP.cjs';
6
6
  import * as zod from 'zod';
7
7
  import * as zod_v4_core from 'zod/v4/core';
8
+ export { A as ADMIN_VERSION } from '../version-DMS0azUx.cjs';
8
9
  import { R as RequireOptions } from '../roles-BjWuj2_v.cjs';
9
10
  import '@supabase/supabase-js';
10
11
 
@@ -190,6 +191,34 @@ type TeamState = {
190
191
  error: string;
191
192
  };
192
193
 
194
+ /**
195
+ * Updating Studio from Studio. A new package version arrives in a site as a
196
+ * Dependabot pull request; this finds it, reads its release notes from the
197
+ * public npm registry, and merges it (owners and staff only) the same way a
198
+ * content change is published — only when the checks are green.
199
+ */
200
+
201
+ interface StudioUpdate {
202
+ /** The pull request that carries the update. */
203
+ number: number;
204
+ htmlUrl: string;
205
+ /** The version the update installs. */
206
+ version: string;
207
+ /** The version running now. */
208
+ currentVersion: string;
209
+ state: "checking" | "ready" | "failed";
210
+ failingCheck: string | null;
211
+ previewUrl: string | null;
212
+ /** Plain-English release notes for `version`, from the npm registry; null when unavailable. */
213
+ notes: string | null;
214
+ }
215
+ /** The version a Dependabot PR bumps @realiizlabs/admin to, from its title or branch; null for any other PR. */
216
+ declare function updateVersionOf(pr: {
217
+ title: string;
218
+ branch: string;
219
+ }): string | null;
220
+ declare function compareVersions(a: string, b: string): number;
221
+
193
222
  declare function createStudio(config: StudioConfig): {
194
223
  config: StudioConfig;
195
224
  isConfigured: () => boolean;
@@ -201,6 +230,7 @@ declare function createStudio(config: StudioConfig): {
201
230
  locate: (path: string) => Located | null;
202
231
  publicUrlFor: (typeId: string, slug: string) => string | null;
203
232
  publicDirFor: (typeId: string) => string;
233
+ studioUpdate: () => Promise<StudioUpdate | null>;
204
234
  listPendingChanges: () => Promise<Map<string, PendingChange>>;
205
235
  pendingFor: (path: string) => Promise<PendingChange | null>;
206
236
  pathOf: (typeId: string, slug: string) => Promise<string | null>;
@@ -274,6 +304,7 @@ declare function createStudio(config: StudioConfig): {
274
304
  type: string;
275
305
  slug: string;
276
306
  }) => Promise<PublishResult | never>;
307
+ updateStudio: (number: number) => Promise<MergeOutcome>;
277
308
  };
278
309
  authHandlers: {
279
310
  callback: (request: Request) => Promise<Response>;
@@ -284,4 +315,4 @@ type Studio = ReturnType<typeof createStudio>;
284
315
  type StudioActions = Studio["actions"];
285
316
  type StudioReaders = Studio["readers"];
286
317
 
287
- export { type ActionResult, type DiscardOutcome, type IdentityConfig, 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, createStudio, pendingLabel, pendingText };
318
+ export { type ActionResult, type DiscardOutcome, type IdentityConfig, 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 StudioUpdate, type TeamState, compareVersions, createStudio, pendingLabel, pendingText, updateVersionOf };
@@ -5,6 +5,7 @@ import { a as PullRequestStatus } from '../types-BP5G1myE.js';
5
5
  import { C as ContentTypeEntry } from '../types-DeHdasdP.js';
6
6
  import * as zod from 'zod';
7
7
  import * as zod_v4_core from 'zod/v4/core';
8
+ export { A as ADMIN_VERSION } from '../version-DMS0azUx.js';
8
9
  import { R as RequireOptions } from '../roles-Dnwj-DAm.js';
9
10
  import '@supabase/supabase-js';
10
11
 
@@ -190,6 +191,34 @@ type TeamState = {
190
191
  error: string;
191
192
  };
192
193
 
194
+ /**
195
+ * Updating Studio from Studio. A new package version arrives in a site as a
196
+ * Dependabot pull request; this finds it, reads its release notes from the
197
+ * public npm registry, and merges it (owners and staff only) the same way a
198
+ * content change is published — only when the checks are green.
199
+ */
200
+
201
+ interface StudioUpdate {
202
+ /** The pull request that carries the update. */
203
+ number: number;
204
+ htmlUrl: string;
205
+ /** The version the update installs. */
206
+ version: string;
207
+ /** The version running now. */
208
+ currentVersion: string;
209
+ state: "checking" | "ready" | "failed";
210
+ failingCheck: string | null;
211
+ previewUrl: string | null;
212
+ /** Plain-English release notes for `version`, from the npm registry; null when unavailable. */
213
+ notes: string | null;
214
+ }
215
+ /** The version a Dependabot PR bumps @realiizlabs/admin to, from its title or branch; null for any other PR. */
216
+ declare function updateVersionOf(pr: {
217
+ title: string;
218
+ branch: string;
219
+ }): string | null;
220
+ declare function compareVersions(a: string, b: string): number;
221
+
193
222
  declare function createStudio(config: StudioConfig): {
194
223
  config: StudioConfig;
195
224
  isConfigured: () => boolean;
@@ -201,6 +230,7 @@ declare function createStudio(config: StudioConfig): {
201
230
  locate: (path: string) => Located | null;
202
231
  publicUrlFor: (typeId: string, slug: string) => string | null;
203
232
  publicDirFor: (typeId: string) => string;
233
+ studioUpdate: () => Promise<StudioUpdate | null>;
204
234
  listPendingChanges: () => Promise<Map<string, PendingChange>>;
205
235
  pendingFor: (path: string) => Promise<PendingChange | null>;
206
236
  pathOf: (typeId: string, slug: string) => Promise<string | null>;
@@ -274,6 +304,7 @@ declare function createStudio(config: StudioConfig): {
274
304
  type: string;
275
305
  slug: string;
276
306
  }) => Promise<PublishResult | never>;
307
+ updateStudio: (number: number) => Promise<MergeOutcome>;
277
308
  };
278
309
  authHandlers: {
279
310
  callback: (request: Request) => Promise<Response>;
@@ -284,4 +315,4 @@ type Studio = ReturnType<typeof createStudio>;
284
315
  type StudioActions = Studio["actions"];
285
316
  type StudioReaders = Studio["readers"];
286
317
 
287
- export { type ActionResult, type DiscardOutcome, type IdentityConfig, 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, createStudio, pendingLabel, pendingText };
318
+ export { type ActionResult, type DiscardOutcome, type IdentityConfig, 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 StudioUpdate, type TeamState, compareVersions, createStudio, pendingLabel, pendingText, updateVersionOf };
@@ -1095,18 +1095,93 @@ function makeHandlers(ctx) {
1095
1095
  await signOut(await ctx.identityClient());
1096
1096
  } catch {
1097
1097
  }
1098
- return Response.redirect(new URL("/admin/sign-in", request.url), 303);
1098
+ const res = Response.redirect(new URL("/admin/sign-in", request.url), 303);
1099
+ const headers = new Headers(res.headers);
1100
+ headers.append("Set-Cookie", "rz-studio=; Path=/; Max-Age=0; SameSite=Lax");
1101
+ return new Response(null, { status: 303, headers });
1099
1102
  };
1100
1103
  return { callback, signout };
1101
1104
  }
1102
1105
 
1106
+ // src/version.ts
1107
+ var ADMIN_VERSION = "0.15.0" ;
1108
+
1109
+ // src/studio/update.ts
1110
+ var PACKAGE_NAME = "@realiizlabs/admin";
1111
+ var DEPENDABOT_PREFIX = "dependabot/npm_and_yarn/";
1112
+ var TITLE_RE = /@realiizlabs\/admin\b.*?\bto\s+v?(\d+\.\d+\.\d+)/i;
1113
+ var BRANCH_RE2 = /realiizlabs\/admin-(\d+\.\d+\.\d+)$/;
1114
+ function updateVersionOf(pr) {
1115
+ if (!pr.branch.startsWith(DEPENDABOT_PREFIX)) return null;
1116
+ const fromTitle = TITLE_RE.exec(pr.title)?.[1];
1117
+ if (fromTitle) return fromTitle;
1118
+ return BRANCH_RE2.exec(pr.branch)?.[1] ?? null;
1119
+ }
1120
+ async function fetchReleaseNotes(version, fetchImpl = fetch) {
1121
+ try {
1122
+ const res = await fetchImpl(`https://registry.npmjs.org/${PACKAGE_NAME}/${version}`, { headers: { accept: "application/json" } });
1123
+ if (!res.ok) return null;
1124
+ const json = await res.json();
1125
+ return typeof json.releaseNotes === "string" && json.releaseNotes.trim() ? json.releaseNotes.trim() : null;
1126
+ } catch {
1127
+ return null;
1128
+ }
1129
+ }
1130
+ function stateOf(status) {
1131
+ if (status.state === "green") return { state: "ready", failingCheck: null, previewUrl: status.previewUrl ?? null };
1132
+ if (status.state === "red") return { state: "failed", failingCheck: status.failingCheck ?? null, previewUrl: null };
1133
+ return { state: "checking", failingCheck: null, previewUrl: null };
1134
+ }
1135
+ function makeUpdate(config, ctx, deps = {}) {
1136
+ const revalidate = config.revalidate ?? (() => {
1137
+ });
1138
+ const studioUpdate = async () => {
1139
+ try {
1140
+ const repo = config.env().repo;
1141
+ const prs = await listOpenPullRequests(repo, { headPrefix: DEPENDABOT_PREFIX });
1142
+ const candidates = prs.map((pr2) => ({ pr: pr2, version: updateVersionOf(pr2) })).filter((c) => c.version !== null);
1143
+ if (candidates.length === 0) return null;
1144
+ candidates.sort((a, b) => compareVersions(b.version, a.version));
1145
+ const { pr, version } = candidates[0];
1146
+ const [status, notes] = await Promise.all([getPullRequestStatus(repo, pr.number), fetchReleaseNotes(version, deps.fetch)]);
1147
+ return { number: pr.number, htmlUrl: pr.html_url, version, currentVersion: ADMIN_VERSION, notes, ...stateOf(status) };
1148
+ } catch {
1149
+ return null;
1150
+ }
1151
+ };
1152
+ const updateStudio = async (number) => {
1153
+ await ctx.requireAdmin({ minimumRole: "owner" });
1154
+ const update = await studioUpdate();
1155
+ if (!update || update.number !== number) return { state: "error", message: "That isn\u2019t a Studio update." };
1156
+ try {
1157
+ const result = await mergePullRequest(config.env().repo, number, { requireGreen: true });
1158
+ revalidate("/admin");
1159
+ return result.merged ? { state: "merged", sha: result.sha } : { state: "pending" };
1160
+ } catch (err) {
1161
+ if (err instanceof StaleBaseError) return { state: "stale" };
1162
+ if (err instanceof ChecksFailedError) return { state: "red", failingCheck: err.failingCheck };
1163
+ return { state: "error", message: err instanceof Error ? err.message : String(err) };
1164
+ }
1165
+ };
1166
+ return { studioUpdate, updateStudio };
1167
+ }
1168
+ function compareVersions(a, b) {
1169
+ const pa = a.split(".").map(Number), pb = b.split(".").map(Number);
1170
+ for (let i = 0; i < 3; i++) {
1171
+ if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);
1172
+ }
1173
+ return 0;
1174
+ }
1175
+
1103
1176
  // src/studio/index.ts
1104
1177
  function createStudio(config) {
1105
1178
  const ctx = makeContext(config);
1106
1179
  const registry = makeRegistry(config);
1107
1180
  const pending = makePending(config, registry);
1108
1181
  const readers = makeReaders(config, registry, pending);
1182
+ const update = makeUpdate(config, ctx);
1109
1183
  const actions = {
1184
+ updateStudio: update.updateStudio,
1110
1185
  ...makePublish(config, registry, ctx, readers),
1111
1186
  ...makeMerge(config, ctx, readers),
1112
1187
  ...makePreview(ctx),
@@ -1120,12 +1195,12 @@ function createStudio(config) {
1120
1195
  entries: registry.entries,
1121
1196
  requireAdmin: ctx.requireAdmin,
1122
1197
  identityClient: ctx.identityClient,
1123
- readers: { ...readers, ...pending, locate: registry.locate, publicUrlFor: registry.publicUrlFor, publicDirFor: registry.publicDirFor },
1198
+ readers: { ...readers, ...pending, locate: registry.locate, publicUrlFor: registry.publicUrlFor, publicDirFor: registry.publicDirFor, studioUpdate: update.studioUpdate },
1124
1199
  actions,
1125
1200
  authHandlers: makeHandlers(ctx)
1126
1201
  };
1127
1202
  }
1128
1203
 
1129
- export { IMAGE_ERRORS, IMAGE_LIMITS, NotHere, createStudio, decodedSize, pendingLabel, pendingText, validateImages };
1204
+ export { ADMIN_VERSION, IMAGE_ERRORS, IMAGE_LIMITS, NotHere, compareVersions, createStudio, decodedSize, pendingLabel, pendingText, updateVersionOf, validateImages };
1130
1205
  //# sourceMappingURL=index.js.map
1131
1206
  //# sourceMappingURL=index.js.map