@neopress/mcp 1.0.0 → 1.1.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/bin.cjs CHANGED
@@ -51345,13 +51345,33 @@ function serializeTemplate(t) {
51345
51345
  id: t.id,
51346
51346
  name: t.name,
51347
51347
  description: t.description ?? null,
51348
- industry: t.industry,
51349
51348
  referenceUrl: t.reference_url,
51350
51349
  previewImgUrls: t.preview_img_urls,
51351
51350
  previewVideoUrl: t.preview_video_url ?? null,
51352
51351
  createdAt: t.created_at
51353
51352
  };
51354
51353
  }
51354
+ function serializeHeadshot(h) {
51355
+ return {
51356
+ id: h.id,
51357
+ imageUrl: h.image_url,
51358
+ width: h.width,
51359
+ height: h.height,
51360
+ aspectRatio: h.aspect_ratio,
51361
+ mimeType: h.mime_type,
51362
+ byteSize: h.byte_size,
51363
+ ageBand: h.age_band,
51364
+ gender: h.gender,
51365
+ ethnicity: h.ethnicity,
51366
+ attire: h.attire,
51367
+ variationIndex: h.variation_index,
51368
+ gptPrompt: h.gpt_prompt,
51369
+ tags: h.tags ?? [],
51370
+ isActive: h.is_active,
51371
+ createdAt: h.created_at,
51372
+ updatedAt: h.updated_at
51373
+ };
51374
+ }
51355
51375
  function serializeSite(site) {
51356
51376
  return {
51357
51377
  id: site.id,
@@ -51405,7 +51425,8 @@ var PagesEndpoint = class {
51405
51425
  }
51406
51426
  async list(params) {
51407
51427
  const { page, limit, offset } = this.client.resolvePagination(params);
51408
- let query = this.client.db.from("pages").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("path");
51428
+ let query = this.client.db.from("pages").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
51429
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("path");
51409
51430
  if (params?.status)
51410
51431
  query = query.eq("status", params.status);
51411
51432
  const { data, error: error48, count } = await query.range(offset, offset + limit - 1);
@@ -52691,8 +52712,10 @@ var CollectionsEndpoint = class {
52691
52712
  get basePath() {
52692
52713
  return `${this.client.siteBasePath}/collections`;
52693
52714
  }
52694
- async list() {
52695
- const { data, error: error48 } = await this.client.db.from("collections").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("name");
52715
+ async list(params) {
52716
+ let query = this.client.db.from("collections").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
52717
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("name");
52718
+ const { data, error: error48 } = await query;
52696
52719
  if (error48)
52697
52720
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
52698
52721
  return (data ?? []).map((r) => serializeCollection(r));
@@ -52765,7 +52788,7 @@ var EntriesEndpoint = class {
52765
52788
  }
52766
52789
  async list(collectionId, params) {
52767
52790
  const { page, limit, offset } = this.client.resolvePagination(params);
52768
- let query = this.client.db.from("entries").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).eq("collection_id", collectionId).is("deleted_at", null).order("created_at", { ascending: false });
52791
+ let query = this.client.db.from("entries").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).eq("collection_id", collectionId).is("deleted_at", null).order("created_at", { ascending: params?.order === "asc" });
52769
52792
  if (params?.status)
52770
52793
  query = query.eq("status", params.status);
52771
52794
  if (params?.locale)
@@ -52856,8 +52879,10 @@ var FormsEndpoint = class {
52856
52879
  get basePath() {
52857
52880
  return `${this.client.siteBasePath}/forms`;
52858
52881
  }
52859
- async list() {
52860
- const { data, error: error48 } = await this.client.db.from("forms").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("name");
52882
+ async list(params) {
52883
+ let query = this.client.db.from("forms").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
52884
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("name");
52885
+ const { data, error: error48 } = await query;
52861
52886
  if (error48)
52862
52887
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
52863
52888
  const rows = data ?? [];
@@ -52936,7 +52961,7 @@ var AssetsEndpoint = class {
52936
52961
  }
52937
52962
  async list(params) {
52938
52963
  const { page, limit, offset } = this.client.resolvePagination(params);
52939
- const { data, error: error48, count } = await this.client.db.from("assets").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).order("created_at", { ascending: false }).range(offset, offset + limit - 1);
52964
+ const { data, error: error48, count } = await this.client.db.from("assets").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).order("created_at", { ascending: params?.order === "asc" }).range(offset, offset + limit - 1);
52940
52965
  if (error48)
52941
52966
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
52942
52967
  const rows = data ?? [];
@@ -53002,7 +53027,7 @@ var SiteEndpoint = class {
53002
53027
  constructor(client) {
53003
53028
  this.client = client;
53004
53029
  }
53005
- async list() {
53030
+ async list(params) {
53006
53031
  const profileId = this.client.authUserId;
53007
53032
  if (!profileId) {
53008
53033
  throw new NeopressApiError("NO_AUTH", "site.list requires an authenticated access token", 0);
@@ -53010,10 +53035,15 @@ var SiteEndpoint = class {
53010
53035
  const { data, error: error48 } = await this.client.db.from("sites_profiles").select("role, sites!inner(*)").eq("profile_id", profileId).eq("status", "active");
53011
53036
  if (error48)
53012
53037
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53013
- return (data ?? []).map((m) => ({
53038
+ const sites = (data ?? []).map((m) => ({
53014
53039
  ...serializeSite(m.sites),
53015
53040
  role: m.role
53016
53041
  }));
53042
+ if (params?.order) {
53043
+ const dir = params.order === "asc" ? 1 : -1;
53044
+ sites.sort((a, b) => (a.createdAt ?? "") < (b.createdAt ?? "") ? -dir : (a.createdAt ?? "") > (b.createdAt ?? "") ? dir : 0);
53045
+ }
53046
+ return sites;
53017
53047
  }
53018
53048
  async get() {
53019
53049
  const { data, error: error48 } = await this.client.db.from("sites").select("*").eq("id", this.client.siteIdNumber).single();
@@ -53051,8 +53081,10 @@ var RedirectsEndpoint = class {
53051
53081
  constructor(client) {
53052
53082
  this.client = client;
53053
53083
  }
53054
- async list() {
53055
- const { data, error: error48 } = await this.client.db.from("redirect_rules").select("*").eq("site_id", this.client.siteIdNumber).order("priority", { ascending: true });
53084
+ async list(params) {
53085
+ let query = this.client.db.from("redirect_rules").select("*").eq("site_id", this.client.siteIdNumber);
53086
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("priority", { ascending: true });
53087
+ const { data, error: error48 } = await query;
53056
53088
  if (error48)
53057
53089
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53058
53090
  return (data ?? []).map((r) => serializeRedirect(r));
@@ -53062,7 +53094,7 @@ var RedirectsEndpoint = class {
53062
53094
  site_id: this.client.siteIdNumber,
53063
53095
  source: data.source,
53064
53096
  destination: data.destination,
53065
- redirect_type: data.redirectType ?? 307,
53097
+ redirect_type: data.redirectType ?? 308,
53066
53098
  include_children: data.includeChildren ?? false,
53067
53099
  preserve_path: data.preservePath ?? false,
53068
53100
  enabled: data.enabled !== false,
@@ -53129,7 +53161,7 @@ var EntryReferencesEndpoint = class {
53129
53161
  query = query.eq("from_entry_id", params.fromEntryId);
53130
53162
  if (params?.toEntryId)
53131
53163
  query = query.eq("to_entry_id", params.toEntryId);
53132
- const { data, error: error48 } = await query.order("created_at", { ascending: false });
53164
+ const { data, error: error48 } = await query.order("created_at", { ascending: params?.order === "asc" });
53133
53165
  if (error48)
53134
53166
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53135
53167
  return (data ?? []).map((r) => serializeEntryReference(r));
@@ -53251,18 +53283,165 @@ var TemplatesEndpoint = class {
53251
53283
  const res = await this.client.delete(this.basePath);
53252
53284
  return res.data;
53253
53285
  }
53286
+ /**
53287
+ * List templates across the sites the caller can access.
53288
+ *
53289
+ * Direct-DB: `templates` is public-read and `sites` RLS scopes the visible set
53290
+ * (own active memberships, or all sites for the super admin), so no server
53291
+ * route is needed — the two RLS-scoped queries reproduce the old handler.
53292
+ */
53254
53293
  async list(params) {
53255
- const qs = new URLSearchParams();
53256
- if (params?.namePrefix)
53257
- qs.set("namePrefix", params.namePrefix);
53258
- if (params?.industry)
53259
- qs.set("industry", params.industry);
53260
- if (params?.limit != null)
53261
- qs.set("limit", String(params.limit));
53262
- const query = qs.toString();
53263
- const path = query ? `/api/v1/templates?${query}` : "/api/v1/templates";
53264
- const res = await this.client.get(path);
53265
- return res.data;
53294
+ const { data: sitesData, error: sitesError } = await this.client.db.from("sites").select("id, handle, name");
53295
+ if (sitesError)
53296
+ throw new NeopressApiError("DB_ERROR", sitesError.message, 0, sitesError);
53297
+ const siteMeta = /* @__PURE__ */ new Map();
53298
+ for (const site of sitesData ?? []) {
53299
+ if (typeof site.id !== "number")
53300
+ continue;
53301
+ siteMeta.set(site.id, {
53302
+ handle: site.handle ?? null,
53303
+ name: site.name ?? null
53304
+ });
53305
+ }
53306
+ const siteIds = [...siteMeta.keys()];
53307
+ if (siteIds.length === 0)
53308
+ return [];
53309
+ const namePrefix = params?.namePrefix?.trim();
53310
+ const limit = Math.min(Math.max(params?.limit ?? 100, 1), 500);
53311
+ let query = this.client.db.from("templates").select("id, name, reference_url, preview_img_urls, description, created_at").in("id", siteIds);
53312
+ if (namePrefix)
53313
+ query = query.ilike("name", `${namePrefix}%`);
53314
+ if (params?.order)
53315
+ query = query.order("created_at", { ascending: params.order === "asc" });
53316
+ query = query.limit(limit);
53317
+ const { data, error: error48 } = await query;
53318
+ if (error48)
53319
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53320
+ return (data ?? []).map((t) => {
53321
+ const siteId2 = t.id;
53322
+ return {
53323
+ siteId: siteId2,
53324
+ handle: siteMeta.get(siteId2)?.handle ?? null,
53325
+ siteName: siteMeta.get(siteId2)?.name ?? null,
53326
+ name: t.name,
53327
+ referenceUrl: t.reference_url,
53328
+ previewImgUrls: t.preview_img_urls ?? null,
53329
+ description: t.description ?? null,
53330
+ createdAt: t.created_at
53331
+ };
53332
+ });
53333
+ }
53334
+ };
53335
+
53336
+ // ../sdk/dist/endpoints/headshots.js
53337
+ var HeadshotsEndpoint = class {
53338
+ client;
53339
+ constructor(client) {
53340
+ this.client = client;
53341
+ }
53342
+ async list(params) {
53343
+ const { page, limit, offset } = this.client.resolvePagination(params);
53344
+ let query = this.client.db.from("headshot_pool").select("*", { count: "exact" });
53345
+ if (params?.ageBand)
53346
+ query = query.eq("age_band", params.ageBand);
53347
+ if (params?.gender)
53348
+ query = query.eq("gender", params.gender);
53349
+ if (params?.ethnicity)
53350
+ query = query.eq("ethnicity", params.ethnicity);
53351
+ if (params?.attire)
53352
+ query = query.eq("attire", params.attire);
53353
+ if (params?.isActive !== void 0)
53354
+ query = query.eq("is_active", params.isActive);
53355
+ if (params?.order) {
53356
+ query = query.order("created_at", { ascending: params.order === "asc" });
53357
+ } else {
53358
+ query = query.order("age_band", { ascending: true }).order("gender", { ascending: true }).order("ethnicity", { ascending: true }).order("variation_index", { ascending: true });
53359
+ }
53360
+ const { data, error: error48, count } = await query.range(offset, offset + limit - 1);
53361
+ if (error48)
53362
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53363
+ return {
53364
+ data: (data ?? []).map((r) => serializeHeadshot(r)),
53365
+ pagination: this.client.paginationMeta(page, limit, offset, count ?? 0)
53366
+ };
53367
+ }
53368
+ async get(id) {
53369
+ const { data, error: error48 } = await this.client.db.from("headshot_pool").select("*").eq("id", id).single();
53370
+ if (error48)
53371
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53372
+ return serializeHeadshot(data);
53373
+ }
53374
+ async create(params) {
53375
+ const values = {
53376
+ image_url: params.imageUrl,
53377
+ age_band: params.ageBand,
53378
+ gender: params.gender,
53379
+ ethnicity: params.ethnicity,
53380
+ variation_index: params.variationIndex
53381
+ };
53382
+ if (params.attire !== void 0)
53383
+ values.attire = params.attire;
53384
+ if (params.width !== void 0)
53385
+ values.width = params.width;
53386
+ if (params.height !== void 0)
53387
+ values.height = params.height;
53388
+ if (params.aspectRatio !== void 0)
53389
+ values.aspect_ratio = params.aspectRatio;
53390
+ if (params.mimeType !== void 0)
53391
+ values.mime_type = params.mimeType;
53392
+ if (params.byteSize !== void 0)
53393
+ values.byte_size = params.byteSize;
53394
+ if (params.gptPrompt !== void 0)
53395
+ values.gpt_prompt = params.gptPrompt;
53396
+ if (params.tags !== void 0)
53397
+ values.tags = params.tags;
53398
+ if (params.isActive !== void 0)
53399
+ values.is_active = params.isActive;
53400
+ const { data, error: error48 } = await this.client.db.from("headshot_pool").insert(values).select().single();
53401
+ if (error48)
53402
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53403
+ return serializeHeadshot(data);
53404
+ }
53405
+ async update(id, params) {
53406
+ const values = {};
53407
+ if (params.imageUrl !== void 0)
53408
+ values.image_url = params.imageUrl;
53409
+ if (params.ageBand !== void 0)
53410
+ values.age_band = params.ageBand;
53411
+ if (params.gender !== void 0)
53412
+ values.gender = params.gender;
53413
+ if (params.ethnicity !== void 0)
53414
+ values.ethnicity = params.ethnicity;
53415
+ if (params.attire !== void 0)
53416
+ values.attire = params.attire;
53417
+ if (params.variationIndex !== void 0)
53418
+ values.variation_index = params.variationIndex;
53419
+ if (params.width !== void 0)
53420
+ values.width = params.width;
53421
+ if (params.height !== void 0)
53422
+ values.height = params.height;
53423
+ if (params.aspectRatio !== void 0)
53424
+ values.aspect_ratio = params.aspectRatio;
53425
+ if (params.mimeType !== void 0)
53426
+ values.mime_type = params.mimeType;
53427
+ if (params.byteSize !== void 0)
53428
+ values.byte_size = params.byteSize;
53429
+ if (params.gptPrompt !== void 0)
53430
+ values.gpt_prompt = params.gptPrompt;
53431
+ if (params.tags !== void 0)
53432
+ values.tags = params.tags;
53433
+ if (params.isActive !== void 0)
53434
+ values.is_active = params.isActive;
53435
+ const { data, error: error48 } = await this.client.db.from("headshot_pool").update(values).eq("id", id).select().single();
53436
+ if (error48)
53437
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53438
+ return serializeHeadshot(data);
53439
+ }
53440
+ async delete(id) {
53441
+ const { error: error48 } = await this.client.db.from("headshot_pool").delete().eq("id", id);
53442
+ if (error48)
53443
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53444
+ return { id, deleted: true };
53266
53445
  }
53267
53446
  };
53268
53447
 
@@ -53286,6 +53465,7 @@ var NeopressClient = class {
53286
53465
  entryReferences;
53287
53466
  analytics;
53288
53467
  templates;
53468
+ headshots;
53289
53469
  constructor(options) {
53290
53470
  this.baseUrl = (options.baseUrl || "https://app.neopress.ai").replace(/\/$/, "");
53291
53471
  this.accessToken = options.accessToken || "";
@@ -53304,6 +53484,7 @@ var NeopressClient = class {
53304
53484
  this.entryReferences = new EntryReferencesEndpoint(this);
53305
53485
  this.analytics = new AnalyticsEndpoint(this);
53306
53486
  this.templates = new TemplatesEndpoint(this);
53487
+ this.headshots = new HeadshotsEndpoint(this);
53307
53488
  }
53308
53489
  get siteBasePath() {
53309
53490
  return `/api/v1/sites/${this.siteId}`;
@@ -53584,14 +53765,14 @@ Use these rules before generating pages, collection schemas, entries, forms, or
53584
53765
 
53585
53766
  - Layout owns header, footer, global CSS, and 404. Page TSX starts at page content.
53586
53767
  - Generated page TSX is sandboxed. Do not import Supabase, create API clients, read env vars, use filesystem/process APIs, or assume arbitrary npm packages.
53587
- - Use allowed runtime packages only: React/Next basics, shadcn UI imports, lucide-react, date-fns, framer-motion, gsap (incl. \`gsap/ScrollTrigger\`, driven via \`@gsap/react\`'s \`useGSAP\`), @neopress/content, @neopress/routing, @neopress/i18n, and @neopress/forms.
53768
+ - Use allowed runtime packages only: React/Next basics, shadcn UI imports, lucide-react, @radix-ui/react-slot, @neopress/youtube, @neopress/map, clsx, class-variance-authority, tailwind-merge, date-fns, framer-motion, gsap (including \`gsap/ScrollTrigger\`, driven via \`@gsap/react\`'s \`useGSAP\`), react-hook-form, zod, @hookform/resolvers, @hookform/resolvers/zod, @neopress/content, @neopress/routing, @neopress/i18n, and @neopress/forms.
53588
53769
 
53589
53770
  ## Templates
53590
53771
 
53591
53772
  - Reusable templates are normal Neopress sites marked by a \`templates\` metadata row.
53592
53773
  - \`templates.id\` equals \`sites.id\`.
53593
53774
  - Build layout, pages, collections, entries, forms, assets, compile, publish, and verify before marking a finished template.
53594
- - Use \`neopress_template_mark\` to create or update the metadata row with \`name\`, \`industry\`, required \`referenceUrl\`, required \`previewImgUrls\` (at least one), and optional \`description\`.
53775
+ - Use \`neopress_template_mark\` to create or update the metadata row with \`name\`, required \`referenceUrl\`, required \`previewImgUrls\` (at least one), and optional \`description\`.
53595
53776
  - \`referenceUrl\` is a single http(s) provenance URL. \`description\` is an optional summary of the template (max 150 characters).
53596
53777
  - Template metadata does not replace page TSX, CMS content, forms, or publish state.
53597
53778
  - Use \`neopress_template_delete\` only for explicit template-site hard deletion. It removes the metadata row and connected site.
@@ -53601,6 +53782,7 @@ Use these rules before generating pages, collection schemas, entries, forms, or
53601
53782
  - Dynamic pages require a collection, a page path like \`/blog/[slug]\`, a numeric \`collectionId\`, matching entries, entry publish, page compile, and site publish.
53602
53783
  - \`queryConfig\` is stored in page \`draftMeta.queryConfig\`.
53603
53784
  - Query labels become React props. Entry query results are flattened, so use \`post.body\` instead of \`post.data.body\`.
53785
+ - Initial CMS data arrives through those injected query props. \`entryClient\`, when injected in reader/preview for a site handle, is only a client-side auxiliary for load-more/filter/get/create interactions after the initial render; do not use it for initial render or server data loading.
53604
53786
  - Use numeric \`collection_id\` filters. Runtime controls site, status, locale, deleted state, and published snapshots.
53605
53787
 
53606
53788
  ## Collections and entries
@@ -53614,11 +53796,13 @@ Use these rules before generating pages, collection schemas, entries, forms, or
53614
53796
 
53615
53797
  - Reference fields require a schema field with \`type: "references"\` and target \`collection_id\`.
53616
53798
  - Links live in \`entry_references\`, not only in raw entry \`data\`.
53799
+ - \`includeReferences\` fetches arrays keyed by the includeReferences alias or field key, and parent entry \`id\` must be selected. With an alias, the alias prop remains an array of referenced entries. Without an alias, if the field key matches a raw numeric reference field in \`entry.data\`, flattening unwraps that array to one flattened object or null.
53617
53800
  - After changing references, publish the source entry again.
53618
53801
 
53619
53802
  ## Forms
53620
53803
 
53621
53804
  - Render forms with \`NeopressForm\` from \`@neopress/forms\` and the injected \`formClient\` prop.
53805
+ - In page TSX, \`formId\` may be a numeric literal or numeric string so schemas can be injected before compile.
53622
53806
  - Do not manually submit form data with \`fetch\` or \`FormData\`.
53623
53807
  - Create/update the form, publish the form, compile the page, then publish the site.
53624
53808
 
@@ -53936,7 +54120,6 @@ var toolDefinitions = [
53936
54120
  ...siteIdShape,
53937
54121
  name: external_exports.string(),
53938
54122
  description: external_exports.string().max(150).optional(),
53939
- industry: external_exports.string(),
53940
54123
  referenceUrl: httpUrlSchema,
53941
54124
  previewImgUrls: external_exports.array(external_exports.string()).min(1)
53942
54125
  },
@@ -53945,7 +54128,6 @@ var toolDefinitions = [
53945
54128
  return client.templates.mark({
53946
54129
  name: asString(input, "name"),
53947
54130
  description: asOptionalString(input, "description"),
53948
- industry: asString(input, "industry"),
53949
54131
  referenceUrl: asString(input, "referenceUrl"),
53950
54132
  previewImgUrls: input.previewImgUrls
53951
54133
  });
package/dist/http-bin.cjs CHANGED
@@ -52583,13 +52583,33 @@ function serializeTemplate(t) {
52583
52583
  id: t.id,
52584
52584
  name: t.name,
52585
52585
  description: t.description ?? null,
52586
- industry: t.industry,
52587
52586
  referenceUrl: t.reference_url,
52588
52587
  previewImgUrls: t.preview_img_urls,
52589
52588
  previewVideoUrl: t.preview_video_url ?? null,
52590
52589
  createdAt: t.created_at
52591
52590
  };
52592
52591
  }
52592
+ function serializeHeadshot(h) {
52593
+ return {
52594
+ id: h.id,
52595
+ imageUrl: h.image_url,
52596
+ width: h.width,
52597
+ height: h.height,
52598
+ aspectRatio: h.aspect_ratio,
52599
+ mimeType: h.mime_type,
52600
+ byteSize: h.byte_size,
52601
+ ageBand: h.age_band,
52602
+ gender: h.gender,
52603
+ ethnicity: h.ethnicity,
52604
+ attire: h.attire,
52605
+ variationIndex: h.variation_index,
52606
+ gptPrompt: h.gpt_prompt,
52607
+ tags: h.tags ?? [],
52608
+ isActive: h.is_active,
52609
+ createdAt: h.created_at,
52610
+ updatedAt: h.updated_at
52611
+ };
52612
+ }
52593
52613
  function serializeSite(site) {
52594
52614
  return {
52595
52615
  id: site.id,
@@ -52643,7 +52663,8 @@ var PagesEndpoint = class {
52643
52663
  }
52644
52664
  async list(params) {
52645
52665
  const { page, limit, offset } = this.client.resolvePagination(params);
52646
- let query = this.client.db.from("pages").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("path");
52666
+ let query = this.client.db.from("pages").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
52667
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("path");
52647
52668
  if (params?.status)
52648
52669
  query = query.eq("status", params.status);
52649
52670
  const { data, error: error48, count } = await query.range(offset, offset + limit - 1);
@@ -53929,8 +53950,10 @@ var CollectionsEndpoint = class {
53929
53950
  get basePath() {
53930
53951
  return `${this.client.siteBasePath}/collections`;
53931
53952
  }
53932
- async list() {
53933
- const { data, error: error48 } = await this.client.db.from("collections").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("name");
53953
+ async list(params) {
53954
+ let query = this.client.db.from("collections").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
53955
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("name");
53956
+ const { data, error: error48 } = await query;
53934
53957
  if (error48)
53935
53958
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53936
53959
  return (data ?? []).map((r) => serializeCollection(r));
@@ -54003,7 +54026,7 @@ var EntriesEndpoint = class {
54003
54026
  }
54004
54027
  async list(collectionId, params) {
54005
54028
  const { page, limit, offset } = this.client.resolvePagination(params);
54006
- let query = this.client.db.from("entries").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).eq("collection_id", collectionId).is("deleted_at", null).order("created_at", { ascending: false });
54029
+ let query = this.client.db.from("entries").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).eq("collection_id", collectionId).is("deleted_at", null).order("created_at", { ascending: params?.order === "asc" });
54007
54030
  if (params?.status)
54008
54031
  query = query.eq("status", params.status);
54009
54032
  if (params?.locale)
@@ -54094,8 +54117,10 @@ var FormsEndpoint = class {
54094
54117
  get basePath() {
54095
54118
  return `${this.client.siteBasePath}/forms`;
54096
54119
  }
54097
- async list() {
54098
- const { data, error: error48 } = await this.client.db.from("forms").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("name");
54120
+ async list(params) {
54121
+ let query = this.client.db.from("forms").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
54122
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("name");
54123
+ const { data, error: error48 } = await query;
54099
54124
  if (error48)
54100
54125
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
54101
54126
  const rows = data ?? [];
@@ -54174,7 +54199,7 @@ var AssetsEndpoint = class {
54174
54199
  }
54175
54200
  async list(params) {
54176
54201
  const { page, limit, offset } = this.client.resolvePagination(params);
54177
- const { data, error: error48, count } = await this.client.db.from("assets").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).order("created_at", { ascending: false }).range(offset, offset + limit - 1);
54202
+ const { data, error: error48, count } = await this.client.db.from("assets").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).order("created_at", { ascending: params?.order === "asc" }).range(offset, offset + limit - 1);
54178
54203
  if (error48)
54179
54204
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
54180
54205
  const rows = data ?? [];
@@ -54240,7 +54265,7 @@ var SiteEndpoint = class {
54240
54265
  constructor(client) {
54241
54266
  this.client = client;
54242
54267
  }
54243
- async list() {
54268
+ async list(params) {
54244
54269
  const profileId = this.client.authUserId;
54245
54270
  if (!profileId) {
54246
54271
  throw new NeopressApiError("NO_AUTH", "site.list requires an authenticated access token", 0);
@@ -54248,10 +54273,15 @@ var SiteEndpoint = class {
54248
54273
  const { data, error: error48 } = await this.client.db.from("sites_profiles").select("role, sites!inner(*)").eq("profile_id", profileId).eq("status", "active");
54249
54274
  if (error48)
54250
54275
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
54251
- return (data ?? []).map((m) => ({
54276
+ const sites = (data ?? []).map((m) => ({
54252
54277
  ...serializeSite(m.sites),
54253
54278
  role: m.role
54254
54279
  }));
54280
+ if (params?.order) {
54281
+ const dir = params.order === "asc" ? 1 : -1;
54282
+ sites.sort((a, b) => (a.createdAt ?? "") < (b.createdAt ?? "") ? -dir : (a.createdAt ?? "") > (b.createdAt ?? "") ? dir : 0);
54283
+ }
54284
+ return sites;
54255
54285
  }
54256
54286
  async get() {
54257
54287
  const { data, error: error48 } = await this.client.db.from("sites").select("*").eq("id", this.client.siteIdNumber).single();
@@ -54289,8 +54319,10 @@ var RedirectsEndpoint = class {
54289
54319
  constructor(client) {
54290
54320
  this.client = client;
54291
54321
  }
54292
- async list() {
54293
- const { data, error: error48 } = await this.client.db.from("redirect_rules").select("*").eq("site_id", this.client.siteIdNumber).order("priority", { ascending: true });
54322
+ async list(params) {
54323
+ let query = this.client.db.from("redirect_rules").select("*").eq("site_id", this.client.siteIdNumber);
54324
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("priority", { ascending: true });
54325
+ const { data, error: error48 } = await query;
54294
54326
  if (error48)
54295
54327
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
54296
54328
  return (data ?? []).map((r) => serializeRedirect(r));
@@ -54300,7 +54332,7 @@ var RedirectsEndpoint = class {
54300
54332
  site_id: this.client.siteIdNumber,
54301
54333
  source: data.source,
54302
54334
  destination: data.destination,
54303
- redirect_type: data.redirectType ?? 307,
54335
+ redirect_type: data.redirectType ?? 308,
54304
54336
  include_children: data.includeChildren ?? false,
54305
54337
  preserve_path: data.preservePath ?? false,
54306
54338
  enabled: data.enabled !== false,
@@ -54367,7 +54399,7 @@ var EntryReferencesEndpoint = class {
54367
54399
  query = query.eq("from_entry_id", params.fromEntryId);
54368
54400
  if (params?.toEntryId)
54369
54401
  query = query.eq("to_entry_id", params.toEntryId);
54370
- const { data, error: error48 } = await query.order("created_at", { ascending: false });
54402
+ const { data, error: error48 } = await query.order("created_at", { ascending: params?.order === "asc" });
54371
54403
  if (error48)
54372
54404
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
54373
54405
  return (data ?? []).map((r) => serializeEntryReference(r));
@@ -54489,18 +54521,165 @@ var TemplatesEndpoint = class {
54489
54521
  const res = await this.client.delete(this.basePath);
54490
54522
  return res.data;
54491
54523
  }
54524
+ /**
54525
+ * List templates across the sites the caller can access.
54526
+ *
54527
+ * Direct-DB: `templates` is public-read and `sites` RLS scopes the visible set
54528
+ * (own active memberships, or all sites for the super admin), so no server
54529
+ * route is needed — the two RLS-scoped queries reproduce the old handler.
54530
+ */
54492
54531
  async list(params) {
54493
- const qs = new URLSearchParams();
54494
- if (params?.namePrefix)
54495
- qs.set("namePrefix", params.namePrefix);
54496
- if (params?.industry)
54497
- qs.set("industry", params.industry);
54498
- if (params?.limit != null)
54499
- qs.set("limit", String(params.limit));
54500
- const query = qs.toString();
54501
- const path = query ? `/api/v1/templates?${query}` : "/api/v1/templates";
54502
- const res = await this.client.get(path);
54503
- return res.data;
54532
+ const { data: sitesData, error: sitesError } = await this.client.db.from("sites").select("id, handle, name");
54533
+ if (sitesError)
54534
+ throw new NeopressApiError("DB_ERROR", sitesError.message, 0, sitesError);
54535
+ const siteMeta = /* @__PURE__ */ new Map();
54536
+ for (const site of sitesData ?? []) {
54537
+ if (typeof site.id !== "number")
54538
+ continue;
54539
+ siteMeta.set(site.id, {
54540
+ handle: site.handle ?? null,
54541
+ name: site.name ?? null
54542
+ });
54543
+ }
54544
+ const siteIds = [...siteMeta.keys()];
54545
+ if (siteIds.length === 0)
54546
+ return [];
54547
+ const namePrefix = params?.namePrefix?.trim();
54548
+ const limit = Math.min(Math.max(params?.limit ?? 100, 1), 500);
54549
+ let query = this.client.db.from("templates").select("id, name, reference_url, preview_img_urls, description, created_at").in("id", siteIds);
54550
+ if (namePrefix)
54551
+ query = query.ilike("name", `${namePrefix}%`);
54552
+ if (params?.order)
54553
+ query = query.order("created_at", { ascending: params.order === "asc" });
54554
+ query = query.limit(limit);
54555
+ const { data, error: error48 } = await query;
54556
+ if (error48)
54557
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
54558
+ return (data ?? []).map((t) => {
54559
+ const siteId2 = t.id;
54560
+ return {
54561
+ siteId: siteId2,
54562
+ handle: siteMeta.get(siteId2)?.handle ?? null,
54563
+ siteName: siteMeta.get(siteId2)?.name ?? null,
54564
+ name: t.name,
54565
+ referenceUrl: t.reference_url,
54566
+ previewImgUrls: t.preview_img_urls ?? null,
54567
+ description: t.description ?? null,
54568
+ createdAt: t.created_at
54569
+ };
54570
+ });
54571
+ }
54572
+ };
54573
+
54574
+ // ../sdk/dist/endpoints/headshots.js
54575
+ var HeadshotsEndpoint = class {
54576
+ client;
54577
+ constructor(client) {
54578
+ this.client = client;
54579
+ }
54580
+ async list(params) {
54581
+ const { page, limit, offset } = this.client.resolvePagination(params);
54582
+ let query = this.client.db.from("headshot_pool").select("*", { count: "exact" });
54583
+ if (params?.ageBand)
54584
+ query = query.eq("age_band", params.ageBand);
54585
+ if (params?.gender)
54586
+ query = query.eq("gender", params.gender);
54587
+ if (params?.ethnicity)
54588
+ query = query.eq("ethnicity", params.ethnicity);
54589
+ if (params?.attire)
54590
+ query = query.eq("attire", params.attire);
54591
+ if (params?.isActive !== void 0)
54592
+ query = query.eq("is_active", params.isActive);
54593
+ if (params?.order) {
54594
+ query = query.order("created_at", { ascending: params.order === "asc" });
54595
+ } else {
54596
+ query = query.order("age_band", { ascending: true }).order("gender", { ascending: true }).order("ethnicity", { ascending: true }).order("variation_index", { ascending: true });
54597
+ }
54598
+ const { data, error: error48, count } = await query.range(offset, offset + limit - 1);
54599
+ if (error48)
54600
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
54601
+ return {
54602
+ data: (data ?? []).map((r) => serializeHeadshot(r)),
54603
+ pagination: this.client.paginationMeta(page, limit, offset, count ?? 0)
54604
+ };
54605
+ }
54606
+ async get(id) {
54607
+ const { data, error: error48 } = await this.client.db.from("headshot_pool").select("*").eq("id", id).single();
54608
+ if (error48)
54609
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
54610
+ return serializeHeadshot(data);
54611
+ }
54612
+ async create(params) {
54613
+ const values = {
54614
+ image_url: params.imageUrl,
54615
+ age_band: params.ageBand,
54616
+ gender: params.gender,
54617
+ ethnicity: params.ethnicity,
54618
+ variation_index: params.variationIndex
54619
+ };
54620
+ if (params.attire !== void 0)
54621
+ values.attire = params.attire;
54622
+ if (params.width !== void 0)
54623
+ values.width = params.width;
54624
+ if (params.height !== void 0)
54625
+ values.height = params.height;
54626
+ if (params.aspectRatio !== void 0)
54627
+ values.aspect_ratio = params.aspectRatio;
54628
+ if (params.mimeType !== void 0)
54629
+ values.mime_type = params.mimeType;
54630
+ if (params.byteSize !== void 0)
54631
+ values.byte_size = params.byteSize;
54632
+ if (params.gptPrompt !== void 0)
54633
+ values.gpt_prompt = params.gptPrompt;
54634
+ if (params.tags !== void 0)
54635
+ values.tags = params.tags;
54636
+ if (params.isActive !== void 0)
54637
+ values.is_active = params.isActive;
54638
+ const { data, error: error48 } = await this.client.db.from("headshot_pool").insert(values).select().single();
54639
+ if (error48)
54640
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
54641
+ return serializeHeadshot(data);
54642
+ }
54643
+ async update(id, params) {
54644
+ const values = {};
54645
+ if (params.imageUrl !== void 0)
54646
+ values.image_url = params.imageUrl;
54647
+ if (params.ageBand !== void 0)
54648
+ values.age_band = params.ageBand;
54649
+ if (params.gender !== void 0)
54650
+ values.gender = params.gender;
54651
+ if (params.ethnicity !== void 0)
54652
+ values.ethnicity = params.ethnicity;
54653
+ if (params.attire !== void 0)
54654
+ values.attire = params.attire;
54655
+ if (params.variationIndex !== void 0)
54656
+ values.variation_index = params.variationIndex;
54657
+ if (params.width !== void 0)
54658
+ values.width = params.width;
54659
+ if (params.height !== void 0)
54660
+ values.height = params.height;
54661
+ if (params.aspectRatio !== void 0)
54662
+ values.aspect_ratio = params.aspectRatio;
54663
+ if (params.mimeType !== void 0)
54664
+ values.mime_type = params.mimeType;
54665
+ if (params.byteSize !== void 0)
54666
+ values.byte_size = params.byteSize;
54667
+ if (params.gptPrompt !== void 0)
54668
+ values.gpt_prompt = params.gptPrompt;
54669
+ if (params.tags !== void 0)
54670
+ values.tags = params.tags;
54671
+ if (params.isActive !== void 0)
54672
+ values.is_active = params.isActive;
54673
+ const { data, error: error48 } = await this.client.db.from("headshot_pool").update(values).eq("id", id).select().single();
54674
+ if (error48)
54675
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
54676
+ return serializeHeadshot(data);
54677
+ }
54678
+ async delete(id) {
54679
+ const { error: error48 } = await this.client.db.from("headshot_pool").delete().eq("id", id);
54680
+ if (error48)
54681
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
54682
+ return { id, deleted: true };
54504
54683
  }
54505
54684
  };
54506
54685
 
@@ -54524,6 +54703,7 @@ var NeopressClient = class {
54524
54703
  entryReferences;
54525
54704
  analytics;
54526
54705
  templates;
54706
+ headshots;
54527
54707
  constructor(options) {
54528
54708
  this.baseUrl = (options.baseUrl || "https://app.neopress.ai").replace(/\/$/, "");
54529
54709
  this.accessToken = options.accessToken || "";
@@ -54542,6 +54722,7 @@ var NeopressClient = class {
54542
54722
  this.entryReferences = new EntryReferencesEndpoint(this);
54543
54723
  this.analytics = new AnalyticsEndpoint(this);
54544
54724
  this.templates = new TemplatesEndpoint(this);
54725
+ this.headshots = new HeadshotsEndpoint(this);
54545
54726
  }
54546
54727
  get siteBasePath() {
54547
54728
  return `/api/v1/sites/${this.siteId}`;
@@ -54822,14 +55003,14 @@ Use these rules before generating pages, collection schemas, entries, forms, or
54822
55003
 
54823
55004
  - Layout owns header, footer, global CSS, and 404. Page TSX starts at page content.
54824
55005
  - Generated page TSX is sandboxed. Do not import Supabase, create API clients, read env vars, use filesystem/process APIs, or assume arbitrary npm packages.
54825
- - Use allowed runtime packages only: React/Next basics, shadcn UI imports, lucide-react, date-fns, framer-motion, gsap (incl. \`gsap/ScrollTrigger\`, driven via \`@gsap/react\`'s \`useGSAP\`), @neopress/content, @neopress/routing, @neopress/i18n, and @neopress/forms.
55006
+ - Use allowed runtime packages only: React/Next basics, shadcn UI imports, lucide-react, @radix-ui/react-slot, @neopress/youtube, @neopress/map, clsx, class-variance-authority, tailwind-merge, date-fns, framer-motion, gsap (including \`gsap/ScrollTrigger\`, driven via \`@gsap/react\`'s \`useGSAP\`), react-hook-form, zod, @hookform/resolvers, @hookform/resolvers/zod, @neopress/content, @neopress/routing, @neopress/i18n, and @neopress/forms.
54826
55007
 
54827
55008
  ## Templates
54828
55009
 
54829
55010
  - Reusable templates are normal Neopress sites marked by a \`templates\` metadata row.
54830
55011
  - \`templates.id\` equals \`sites.id\`.
54831
55012
  - Build layout, pages, collections, entries, forms, assets, compile, publish, and verify before marking a finished template.
54832
- - Use \`neopress_template_mark\` to create or update the metadata row with \`name\`, \`industry\`, required \`referenceUrl\`, required \`previewImgUrls\` (at least one), and optional \`description\`.
55013
+ - Use \`neopress_template_mark\` to create or update the metadata row with \`name\`, required \`referenceUrl\`, required \`previewImgUrls\` (at least one), and optional \`description\`.
54833
55014
  - \`referenceUrl\` is a single http(s) provenance URL. \`description\` is an optional summary of the template (max 150 characters).
54834
55015
  - Template metadata does not replace page TSX, CMS content, forms, or publish state.
54835
55016
  - Use \`neopress_template_delete\` only for explicit template-site hard deletion. It removes the metadata row and connected site.
@@ -54839,6 +55020,7 @@ Use these rules before generating pages, collection schemas, entries, forms, or
54839
55020
  - Dynamic pages require a collection, a page path like \`/blog/[slug]\`, a numeric \`collectionId\`, matching entries, entry publish, page compile, and site publish.
54840
55021
  - \`queryConfig\` is stored in page \`draftMeta.queryConfig\`.
54841
55022
  - Query labels become React props. Entry query results are flattened, so use \`post.body\` instead of \`post.data.body\`.
55023
+ - Initial CMS data arrives through those injected query props. \`entryClient\`, when injected in reader/preview for a site handle, is only a client-side auxiliary for load-more/filter/get/create interactions after the initial render; do not use it for initial render or server data loading.
54842
55024
  - Use numeric \`collection_id\` filters. Runtime controls site, status, locale, deleted state, and published snapshots.
54843
55025
 
54844
55026
  ## Collections and entries
@@ -54852,11 +55034,13 @@ Use these rules before generating pages, collection schemas, entries, forms, or
54852
55034
 
54853
55035
  - Reference fields require a schema field with \`type: "references"\` and target \`collection_id\`.
54854
55036
  - Links live in \`entry_references\`, not only in raw entry \`data\`.
55037
+ - \`includeReferences\` fetches arrays keyed by the includeReferences alias or field key, and parent entry \`id\` must be selected. With an alias, the alias prop remains an array of referenced entries. Without an alias, if the field key matches a raw numeric reference field in \`entry.data\`, flattening unwraps that array to one flattened object or null.
54855
55038
  - After changing references, publish the source entry again.
54856
55039
 
54857
55040
  ## Forms
54858
55041
 
54859
55042
  - Render forms with \`NeopressForm\` from \`@neopress/forms\` and the injected \`formClient\` prop.
55043
+ - In page TSX, \`formId\` may be a numeric literal or numeric string so schemas can be injected before compile.
54860
55044
  - Do not manually submit form data with \`fetch\` or \`FormData\`.
54861
55045
  - Create/update the form, publish the form, compile the page, then publish the site.
54862
55046
 
@@ -55174,7 +55358,6 @@ var toolDefinitions = [
55174
55358
  ...siteIdShape,
55175
55359
  name: external_exports.string(),
55176
55360
  description: external_exports.string().max(150).optional(),
55177
- industry: external_exports.string(),
55178
55361
  referenceUrl: httpUrlSchema,
55179
55362
  previewImgUrls: external_exports.array(external_exports.string()).min(1)
55180
55363
  },
@@ -55183,7 +55366,6 @@ var toolDefinitions = [
55183
55366
  return client.templates.mark({
55184
55367
  name: asString(input, "name"),
55185
55368
  description: asOptionalString(input, "description"),
55186
- industry: asString(input, "industry"),
55187
55369
  referenceUrl: asString(input, "referenceUrl"),
55188
55370
  previewImgUrls: input.previewImgUrls
55189
55371
  });
package/dist/index.js CHANGED
@@ -51253,13 +51253,33 @@ function serializeTemplate(t) {
51253
51253
  id: t.id,
51254
51254
  name: t.name,
51255
51255
  description: t.description ?? null,
51256
- industry: t.industry,
51257
51256
  referenceUrl: t.reference_url,
51258
51257
  previewImgUrls: t.preview_img_urls,
51259
51258
  previewVideoUrl: t.preview_video_url ?? null,
51260
51259
  createdAt: t.created_at
51261
51260
  };
51262
51261
  }
51262
+ function serializeHeadshot(h) {
51263
+ return {
51264
+ id: h.id,
51265
+ imageUrl: h.image_url,
51266
+ width: h.width,
51267
+ height: h.height,
51268
+ aspectRatio: h.aspect_ratio,
51269
+ mimeType: h.mime_type,
51270
+ byteSize: h.byte_size,
51271
+ ageBand: h.age_band,
51272
+ gender: h.gender,
51273
+ ethnicity: h.ethnicity,
51274
+ attire: h.attire,
51275
+ variationIndex: h.variation_index,
51276
+ gptPrompt: h.gpt_prompt,
51277
+ tags: h.tags ?? [],
51278
+ isActive: h.is_active,
51279
+ createdAt: h.created_at,
51280
+ updatedAt: h.updated_at
51281
+ };
51282
+ }
51263
51283
  function serializeSite(site) {
51264
51284
  return {
51265
51285
  id: site.id,
@@ -51313,7 +51333,8 @@ var PagesEndpoint = class {
51313
51333
  }
51314
51334
  async list(params) {
51315
51335
  const { page, limit, offset } = this.client.resolvePagination(params);
51316
- let query = this.client.db.from("pages").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("path");
51336
+ let query = this.client.db.from("pages").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
51337
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("path");
51317
51338
  if (params?.status)
51318
51339
  query = query.eq("status", params.status);
51319
51340
  const { data, error: error48, count } = await query.range(offset, offset + limit - 1);
@@ -52599,8 +52620,10 @@ var CollectionsEndpoint = class {
52599
52620
  get basePath() {
52600
52621
  return `${this.client.siteBasePath}/collections`;
52601
52622
  }
52602
- async list() {
52603
- const { data, error: error48 } = await this.client.db.from("collections").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("name");
52623
+ async list(params) {
52624
+ let query = this.client.db.from("collections").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
52625
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("name");
52626
+ const { data, error: error48 } = await query;
52604
52627
  if (error48)
52605
52628
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
52606
52629
  return (data ?? []).map((r) => serializeCollection(r));
@@ -52673,7 +52696,7 @@ var EntriesEndpoint = class {
52673
52696
  }
52674
52697
  async list(collectionId, params) {
52675
52698
  const { page, limit, offset } = this.client.resolvePagination(params);
52676
- let query = this.client.db.from("entries").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).eq("collection_id", collectionId).is("deleted_at", null).order("created_at", { ascending: false });
52699
+ let query = this.client.db.from("entries").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).eq("collection_id", collectionId).is("deleted_at", null).order("created_at", { ascending: params?.order === "asc" });
52677
52700
  if (params?.status)
52678
52701
  query = query.eq("status", params.status);
52679
52702
  if (params?.locale)
@@ -52764,8 +52787,10 @@ var FormsEndpoint = class {
52764
52787
  get basePath() {
52765
52788
  return `${this.client.siteBasePath}/forms`;
52766
52789
  }
52767
- async list() {
52768
- const { data, error: error48 } = await this.client.db.from("forms").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("name");
52790
+ async list(params) {
52791
+ let query = this.client.db.from("forms").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
52792
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("name");
52793
+ const { data, error: error48 } = await query;
52769
52794
  if (error48)
52770
52795
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
52771
52796
  const rows = data ?? [];
@@ -52844,7 +52869,7 @@ var AssetsEndpoint = class {
52844
52869
  }
52845
52870
  async list(params) {
52846
52871
  const { page, limit, offset } = this.client.resolvePagination(params);
52847
- const { data, error: error48, count } = await this.client.db.from("assets").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).order("created_at", { ascending: false }).range(offset, offset + limit - 1);
52872
+ const { data, error: error48, count } = await this.client.db.from("assets").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).order("created_at", { ascending: params?.order === "asc" }).range(offset, offset + limit - 1);
52848
52873
  if (error48)
52849
52874
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
52850
52875
  const rows = data ?? [];
@@ -52910,7 +52935,7 @@ var SiteEndpoint = class {
52910
52935
  constructor(client) {
52911
52936
  this.client = client;
52912
52937
  }
52913
- async list() {
52938
+ async list(params) {
52914
52939
  const profileId = this.client.authUserId;
52915
52940
  if (!profileId) {
52916
52941
  throw new NeopressApiError("NO_AUTH", "site.list requires an authenticated access token", 0);
@@ -52918,10 +52943,15 @@ var SiteEndpoint = class {
52918
52943
  const { data, error: error48 } = await this.client.db.from("sites_profiles").select("role, sites!inner(*)").eq("profile_id", profileId).eq("status", "active");
52919
52944
  if (error48)
52920
52945
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
52921
- return (data ?? []).map((m) => ({
52946
+ const sites = (data ?? []).map((m) => ({
52922
52947
  ...serializeSite(m.sites),
52923
52948
  role: m.role
52924
52949
  }));
52950
+ if (params?.order) {
52951
+ const dir = params.order === "asc" ? 1 : -1;
52952
+ sites.sort((a, b) => (a.createdAt ?? "") < (b.createdAt ?? "") ? -dir : (a.createdAt ?? "") > (b.createdAt ?? "") ? dir : 0);
52953
+ }
52954
+ return sites;
52925
52955
  }
52926
52956
  async get() {
52927
52957
  const { data, error: error48 } = await this.client.db.from("sites").select("*").eq("id", this.client.siteIdNumber).single();
@@ -52959,8 +52989,10 @@ var RedirectsEndpoint = class {
52959
52989
  constructor(client) {
52960
52990
  this.client = client;
52961
52991
  }
52962
- async list() {
52963
- const { data, error: error48 } = await this.client.db.from("redirect_rules").select("*").eq("site_id", this.client.siteIdNumber).order("priority", { ascending: true });
52992
+ async list(params) {
52993
+ let query = this.client.db.from("redirect_rules").select("*").eq("site_id", this.client.siteIdNumber);
52994
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("priority", { ascending: true });
52995
+ const { data, error: error48 } = await query;
52964
52996
  if (error48)
52965
52997
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
52966
52998
  return (data ?? []).map((r) => serializeRedirect(r));
@@ -52970,7 +53002,7 @@ var RedirectsEndpoint = class {
52970
53002
  site_id: this.client.siteIdNumber,
52971
53003
  source: data.source,
52972
53004
  destination: data.destination,
52973
- redirect_type: data.redirectType ?? 307,
53005
+ redirect_type: data.redirectType ?? 308,
52974
53006
  include_children: data.includeChildren ?? false,
52975
53007
  preserve_path: data.preservePath ?? false,
52976
53008
  enabled: data.enabled !== false,
@@ -53037,7 +53069,7 @@ var EntryReferencesEndpoint = class {
53037
53069
  query = query.eq("from_entry_id", params.fromEntryId);
53038
53070
  if (params?.toEntryId)
53039
53071
  query = query.eq("to_entry_id", params.toEntryId);
53040
- const { data, error: error48 } = await query.order("created_at", { ascending: false });
53072
+ const { data, error: error48 } = await query.order("created_at", { ascending: params?.order === "asc" });
53041
53073
  if (error48)
53042
53074
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53043
53075
  return (data ?? []).map((r) => serializeEntryReference(r));
@@ -53159,18 +53191,165 @@ var TemplatesEndpoint = class {
53159
53191
  const res = await this.client.delete(this.basePath);
53160
53192
  return res.data;
53161
53193
  }
53194
+ /**
53195
+ * List templates across the sites the caller can access.
53196
+ *
53197
+ * Direct-DB: `templates` is public-read and `sites` RLS scopes the visible set
53198
+ * (own active memberships, or all sites for the super admin), so no server
53199
+ * route is needed — the two RLS-scoped queries reproduce the old handler.
53200
+ */
53162
53201
  async list(params) {
53163
- const qs = new URLSearchParams();
53164
- if (params?.namePrefix)
53165
- qs.set("namePrefix", params.namePrefix);
53166
- if (params?.industry)
53167
- qs.set("industry", params.industry);
53168
- if (params?.limit != null)
53169
- qs.set("limit", String(params.limit));
53170
- const query = qs.toString();
53171
- const path = query ? `/api/v1/templates?${query}` : "/api/v1/templates";
53172
- const res = await this.client.get(path);
53173
- return res.data;
53202
+ const { data: sitesData, error: sitesError } = await this.client.db.from("sites").select("id, handle, name");
53203
+ if (sitesError)
53204
+ throw new NeopressApiError("DB_ERROR", sitesError.message, 0, sitesError);
53205
+ const siteMeta = /* @__PURE__ */ new Map();
53206
+ for (const site of sitesData ?? []) {
53207
+ if (typeof site.id !== "number")
53208
+ continue;
53209
+ siteMeta.set(site.id, {
53210
+ handle: site.handle ?? null,
53211
+ name: site.name ?? null
53212
+ });
53213
+ }
53214
+ const siteIds = [...siteMeta.keys()];
53215
+ if (siteIds.length === 0)
53216
+ return [];
53217
+ const namePrefix = params?.namePrefix?.trim();
53218
+ const limit = Math.min(Math.max(params?.limit ?? 100, 1), 500);
53219
+ let query = this.client.db.from("templates").select("id, name, reference_url, preview_img_urls, description, created_at").in("id", siteIds);
53220
+ if (namePrefix)
53221
+ query = query.ilike("name", `${namePrefix}%`);
53222
+ if (params?.order)
53223
+ query = query.order("created_at", { ascending: params.order === "asc" });
53224
+ query = query.limit(limit);
53225
+ const { data, error: error48 } = await query;
53226
+ if (error48)
53227
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53228
+ return (data ?? []).map((t) => {
53229
+ const siteId2 = t.id;
53230
+ return {
53231
+ siteId: siteId2,
53232
+ handle: siteMeta.get(siteId2)?.handle ?? null,
53233
+ siteName: siteMeta.get(siteId2)?.name ?? null,
53234
+ name: t.name,
53235
+ referenceUrl: t.reference_url,
53236
+ previewImgUrls: t.preview_img_urls ?? null,
53237
+ description: t.description ?? null,
53238
+ createdAt: t.created_at
53239
+ };
53240
+ });
53241
+ }
53242
+ };
53243
+
53244
+ // ../sdk/dist/endpoints/headshots.js
53245
+ var HeadshotsEndpoint = class {
53246
+ client;
53247
+ constructor(client) {
53248
+ this.client = client;
53249
+ }
53250
+ async list(params) {
53251
+ const { page, limit, offset } = this.client.resolvePagination(params);
53252
+ let query = this.client.db.from("headshot_pool").select("*", { count: "exact" });
53253
+ if (params?.ageBand)
53254
+ query = query.eq("age_band", params.ageBand);
53255
+ if (params?.gender)
53256
+ query = query.eq("gender", params.gender);
53257
+ if (params?.ethnicity)
53258
+ query = query.eq("ethnicity", params.ethnicity);
53259
+ if (params?.attire)
53260
+ query = query.eq("attire", params.attire);
53261
+ if (params?.isActive !== void 0)
53262
+ query = query.eq("is_active", params.isActive);
53263
+ if (params?.order) {
53264
+ query = query.order("created_at", { ascending: params.order === "asc" });
53265
+ } else {
53266
+ query = query.order("age_band", { ascending: true }).order("gender", { ascending: true }).order("ethnicity", { ascending: true }).order("variation_index", { ascending: true });
53267
+ }
53268
+ const { data, error: error48, count } = await query.range(offset, offset + limit - 1);
53269
+ if (error48)
53270
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53271
+ return {
53272
+ data: (data ?? []).map((r) => serializeHeadshot(r)),
53273
+ pagination: this.client.paginationMeta(page, limit, offset, count ?? 0)
53274
+ };
53275
+ }
53276
+ async get(id) {
53277
+ const { data, error: error48 } = await this.client.db.from("headshot_pool").select("*").eq("id", id).single();
53278
+ if (error48)
53279
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53280
+ return serializeHeadshot(data);
53281
+ }
53282
+ async create(params) {
53283
+ const values = {
53284
+ image_url: params.imageUrl,
53285
+ age_band: params.ageBand,
53286
+ gender: params.gender,
53287
+ ethnicity: params.ethnicity,
53288
+ variation_index: params.variationIndex
53289
+ };
53290
+ if (params.attire !== void 0)
53291
+ values.attire = params.attire;
53292
+ if (params.width !== void 0)
53293
+ values.width = params.width;
53294
+ if (params.height !== void 0)
53295
+ values.height = params.height;
53296
+ if (params.aspectRatio !== void 0)
53297
+ values.aspect_ratio = params.aspectRatio;
53298
+ if (params.mimeType !== void 0)
53299
+ values.mime_type = params.mimeType;
53300
+ if (params.byteSize !== void 0)
53301
+ values.byte_size = params.byteSize;
53302
+ if (params.gptPrompt !== void 0)
53303
+ values.gpt_prompt = params.gptPrompt;
53304
+ if (params.tags !== void 0)
53305
+ values.tags = params.tags;
53306
+ if (params.isActive !== void 0)
53307
+ values.is_active = params.isActive;
53308
+ const { data, error: error48 } = await this.client.db.from("headshot_pool").insert(values).select().single();
53309
+ if (error48)
53310
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53311
+ return serializeHeadshot(data);
53312
+ }
53313
+ async update(id, params) {
53314
+ const values = {};
53315
+ if (params.imageUrl !== void 0)
53316
+ values.image_url = params.imageUrl;
53317
+ if (params.ageBand !== void 0)
53318
+ values.age_band = params.ageBand;
53319
+ if (params.gender !== void 0)
53320
+ values.gender = params.gender;
53321
+ if (params.ethnicity !== void 0)
53322
+ values.ethnicity = params.ethnicity;
53323
+ if (params.attire !== void 0)
53324
+ values.attire = params.attire;
53325
+ if (params.variationIndex !== void 0)
53326
+ values.variation_index = params.variationIndex;
53327
+ if (params.width !== void 0)
53328
+ values.width = params.width;
53329
+ if (params.height !== void 0)
53330
+ values.height = params.height;
53331
+ if (params.aspectRatio !== void 0)
53332
+ values.aspect_ratio = params.aspectRatio;
53333
+ if (params.mimeType !== void 0)
53334
+ values.mime_type = params.mimeType;
53335
+ if (params.byteSize !== void 0)
53336
+ values.byte_size = params.byteSize;
53337
+ if (params.gptPrompt !== void 0)
53338
+ values.gpt_prompt = params.gptPrompt;
53339
+ if (params.tags !== void 0)
53340
+ values.tags = params.tags;
53341
+ if (params.isActive !== void 0)
53342
+ values.is_active = params.isActive;
53343
+ const { data, error: error48 } = await this.client.db.from("headshot_pool").update(values).eq("id", id).select().single();
53344
+ if (error48)
53345
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53346
+ return serializeHeadshot(data);
53347
+ }
53348
+ async delete(id) {
53349
+ const { error: error48 } = await this.client.db.from("headshot_pool").delete().eq("id", id);
53350
+ if (error48)
53351
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
53352
+ return { id, deleted: true };
53174
53353
  }
53175
53354
  };
53176
53355
 
@@ -53194,6 +53373,7 @@ var NeopressClient = class {
53194
53373
  entryReferences;
53195
53374
  analytics;
53196
53375
  templates;
53376
+ headshots;
53197
53377
  constructor(options) {
53198
53378
  this.baseUrl = (options.baseUrl || "https://app.neopress.ai").replace(/\/$/, "");
53199
53379
  this.accessToken = options.accessToken || "";
@@ -53212,6 +53392,7 @@ var NeopressClient = class {
53212
53392
  this.entryReferences = new EntryReferencesEndpoint(this);
53213
53393
  this.analytics = new AnalyticsEndpoint(this);
53214
53394
  this.templates = new TemplatesEndpoint(this);
53395
+ this.headshots = new HeadshotsEndpoint(this);
53215
53396
  }
53216
53397
  get siteBasePath() {
53217
53398
  return `/api/v1/sites/${this.siteId}`;
@@ -53492,14 +53673,14 @@ Use these rules before generating pages, collection schemas, entries, forms, or
53492
53673
 
53493
53674
  - Layout owns header, footer, global CSS, and 404. Page TSX starts at page content.
53494
53675
  - Generated page TSX is sandboxed. Do not import Supabase, create API clients, read env vars, use filesystem/process APIs, or assume arbitrary npm packages.
53495
- - Use allowed runtime packages only: React/Next basics, shadcn UI imports, lucide-react, date-fns, framer-motion, gsap (incl. \`gsap/ScrollTrigger\`, driven via \`@gsap/react\`'s \`useGSAP\`), @neopress/content, @neopress/routing, @neopress/i18n, and @neopress/forms.
53676
+ - Use allowed runtime packages only: React/Next basics, shadcn UI imports, lucide-react, @radix-ui/react-slot, @neopress/youtube, @neopress/map, clsx, class-variance-authority, tailwind-merge, date-fns, framer-motion, gsap (including \`gsap/ScrollTrigger\`, driven via \`@gsap/react\`'s \`useGSAP\`), react-hook-form, zod, @hookform/resolvers, @hookform/resolvers/zod, @neopress/content, @neopress/routing, @neopress/i18n, and @neopress/forms.
53496
53677
 
53497
53678
  ## Templates
53498
53679
 
53499
53680
  - Reusable templates are normal Neopress sites marked by a \`templates\` metadata row.
53500
53681
  - \`templates.id\` equals \`sites.id\`.
53501
53682
  - Build layout, pages, collections, entries, forms, assets, compile, publish, and verify before marking a finished template.
53502
- - Use \`neopress_template_mark\` to create or update the metadata row with \`name\`, \`industry\`, required \`referenceUrl\`, required \`previewImgUrls\` (at least one), and optional \`description\`.
53683
+ - Use \`neopress_template_mark\` to create or update the metadata row with \`name\`, required \`referenceUrl\`, required \`previewImgUrls\` (at least one), and optional \`description\`.
53503
53684
  - \`referenceUrl\` is a single http(s) provenance URL. \`description\` is an optional summary of the template (max 150 characters).
53504
53685
  - Template metadata does not replace page TSX, CMS content, forms, or publish state.
53505
53686
  - Use \`neopress_template_delete\` only for explicit template-site hard deletion. It removes the metadata row and connected site.
@@ -53509,6 +53690,7 @@ Use these rules before generating pages, collection schemas, entries, forms, or
53509
53690
  - Dynamic pages require a collection, a page path like \`/blog/[slug]\`, a numeric \`collectionId\`, matching entries, entry publish, page compile, and site publish.
53510
53691
  - \`queryConfig\` is stored in page \`draftMeta.queryConfig\`.
53511
53692
  - Query labels become React props. Entry query results are flattened, so use \`post.body\` instead of \`post.data.body\`.
53693
+ - Initial CMS data arrives through those injected query props. \`entryClient\`, when injected in reader/preview for a site handle, is only a client-side auxiliary for load-more/filter/get/create interactions after the initial render; do not use it for initial render or server data loading.
53512
53694
  - Use numeric \`collection_id\` filters. Runtime controls site, status, locale, deleted state, and published snapshots.
53513
53695
 
53514
53696
  ## Collections and entries
@@ -53522,11 +53704,13 @@ Use these rules before generating pages, collection schemas, entries, forms, or
53522
53704
 
53523
53705
  - Reference fields require a schema field with \`type: "references"\` and target \`collection_id\`.
53524
53706
  - Links live in \`entry_references\`, not only in raw entry \`data\`.
53707
+ - \`includeReferences\` fetches arrays keyed by the includeReferences alias or field key, and parent entry \`id\` must be selected. With an alias, the alias prop remains an array of referenced entries. Without an alias, if the field key matches a raw numeric reference field in \`entry.data\`, flattening unwraps that array to one flattened object or null.
53525
53708
  - After changing references, publish the source entry again.
53526
53709
 
53527
53710
  ## Forms
53528
53711
 
53529
53712
  - Render forms with \`NeopressForm\` from \`@neopress/forms\` and the injected \`formClient\` prop.
53713
+ - In page TSX, \`formId\` may be a numeric literal or numeric string so schemas can be injected before compile.
53530
53714
  - Do not manually submit form data with \`fetch\` or \`FormData\`.
53531
53715
  - Create/update the form, publish the form, compile the page, then publish the site.
53532
53716
 
@@ -53844,7 +54028,6 @@ var toolDefinitions = [
53844
54028
  ...siteIdShape,
53845
54029
  name: external_exports3.string(),
53846
54030
  description: external_exports3.string().max(150).optional(),
53847
- industry: external_exports3.string(),
53848
54031
  referenceUrl: httpUrlSchema,
53849
54032
  previewImgUrls: external_exports3.array(external_exports3.string()).min(1)
53850
54033
  },
@@ -53853,7 +54036,6 @@ var toolDefinitions = [
53853
54036
  return client.templates.mark({
53854
54037
  name: asString(input, "name"),
53855
54038
  description: asOptionalString(input, "description"),
53856
- industry: asString(input, "industry"),
53857
54039
  referenceUrl: asString(input, "referenceUrl"),
53858
54040
  previewImgUrls: input.previewImgUrls
53859
54041
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neopress/mcp",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Neopress MCP server for first-party AI agents",
5
5
  "type": "module",
6
6
  "bin": {