@neopress/cli 4.2.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/bin.cjs +113 -45
  2. package/dist/index.js +80 -24
  3. package/package.json +1 -1
package/dist/bin.cjs CHANGED
@@ -24998,7 +24998,8 @@ var PagesEndpoint = class {
24998
24998
  }
24999
24999
  async list(params) {
25000
25000
  const { page, limit, offset } = this.client.resolvePagination(params);
25001
- let query = this.client.db.from("pages").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("path");
25001
+ let query = this.client.db.from("pages").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
25002
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("path");
25002
25003
  if (params?.status)
25003
25004
  query = query.eq("status", params.status);
25004
25005
  const { data, error: error48, count } = await query.range(offset, offset + limit - 1);
@@ -40052,8 +40053,10 @@ var CollectionsEndpoint = class {
40052
40053
  get basePath() {
40053
40054
  return `${this.client.siteBasePath}/collections`;
40054
40055
  }
40055
- async list() {
40056
- const { data, error: error48 } = await this.client.db.from("collections").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("name");
40056
+ async list(params) {
40057
+ let query = this.client.db.from("collections").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
40058
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("name");
40059
+ const { data, error: error48 } = await query;
40057
40060
  if (error48)
40058
40061
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40059
40062
  return (data ?? []).map((r) => serializeCollection(r));
@@ -40126,7 +40129,7 @@ var EntriesEndpoint = class {
40126
40129
  }
40127
40130
  async list(collectionId, params) {
40128
40131
  const { page, limit, offset } = this.client.resolvePagination(params);
40129
- 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 });
40132
+ 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" });
40130
40133
  if (params?.status)
40131
40134
  query = query.eq("status", params.status);
40132
40135
  if (params?.locale)
@@ -40217,8 +40220,10 @@ var FormsEndpoint = class {
40217
40220
  get basePath() {
40218
40221
  return `${this.client.siteBasePath}/forms`;
40219
40222
  }
40220
- async list() {
40221
- const { data, error: error48 } = await this.client.db.from("forms").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("name");
40223
+ async list(params) {
40224
+ let query = this.client.db.from("forms").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
40225
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("name");
40226
+ const { data, error: error48 } = await query;
40222
40227
  if (error48)
40223
40228
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40224
40229
  const rows = data ?? [];
@@ -40297,7 +40302,7 @@ var AssetsEndpoint = class {
40297
40302
  }
40298
40303
  async list(params) {
40299
40304
  const { page, limit, offset } = this.client.resolvePagination(params);
40300
- 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);
40305
+ 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);
40301
40306
  if (error48)
40302
40307
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40303
40308
  const rows = data ?? [];
@@ -40363,7 +40368,7 @@ var SiteEndpoint = class {
40363
40368
  constructor(client) {
40364
40369
  this.client = client;
40365
40370
  }
40366
- async list() {
40371
+ async list(params) {
40367
40372
  const profileId = this.client.authUserId;
40368
40373
  if (!profileId) {
40369
40374
  throw new NeopressApiError("NO_AUTH", "site.list requires an authenticated access token", 0);
@@ -40371,10 +40376,15 @@ var SiteEndpoint = class {
40371
40376
  const { data, error: error48 } = await this.client.db.from("sites_profiles").select("role, sites!inner(*)").eq("profile_id", profileId).eq("status", "active");
40372
40377
  if (error48)
40373
40378
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40374
- return (data ?? []).map((m) => ({
40379
+ const sites = (data ?? []).map((m) => ({
40375
40380
  ...serializeSite(m.sites),
40376
40381
  role: m.role
40377
40382
  }));
40383
+ if (params?.order) {
40384
+ const dir = params.order === "asc" ? 1 : -1;
40385
+ sites.sort((a, b) => (a.createdAt ?? "") < (b.createdAt ?? "") ? -dir : (a.createdAt ?? "") > (b.createdAt ?? "") ? dir : 0);
40386
+ }
40387
+ return sites;
40378
40388
  }
40379
40389
  async get() {
40380
40390
  const { data, error: error48 } = await this.client.db.from("sites").select("*").eq("id", this.client.siteIdNumber).single();
@@ -40412,8 +40422,10 @@ var RedirectsEndpoint = class {
40412
40422
  constructor(client) {
40413
40423
  this.client = client;
40414
40424
  }
40415
- async list() {
40416
- const { data, error: error48 } = await this.client.db.from("redirect_rules").select("*").eq("site_id", this.client.siteIdNumber).order("priority", { ascending: true });
40425
+ async list(params) {
40426
+ let query = this.client.db.from("redirect_rules").select("*").eq("site_id", this.client.siteIdNumber);
40427
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("priority", { ascending: true });
40428
+ const { data, error: error48 } = await query;
40417
40429
  if (error48)
40418
40430
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40419
40431
  return (data ?? []).map((r) => serializeRedirect(r));
@@ -40490,7 +40502,7 @@ var EntryReferencesEndpoint = class {
40490
40502
  query = query.eq("from_entry_id", params.fromEntryId);
40491
40503
  if (params?.toEntryId)
40492
40504
  query = query.eq("to_entry_id", params.toEntryId);
40493
- const { data, error: error48 } = await query.order("created_at", { ascending: false });
40505
+ const { data, error: error48 } = await query.order("created_at", { ascending: params?.order === "asc" });
40494
40506
  if (error48)
40495
40507
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40496
40508
  return (data ?? []).map((r) => serializeEntryReference(r));
@@ -40612,18 +40624,57 @@ var TemplatesEndpoint = class {
40612
40624
  const res = await this.client.delete(this.basePath);
40613
40625
  return res.data;
40614
40626
  }
40627
+ /**
40628
+ * List templates across the sites the caller can access.
40629
+ *
40630
+ * Direct-DB: `templates` is public-read and `sites` RLS scopes the visible set
40631
+ * (own active memberships, or all sites for the super admin), so no server
40632
+ * route is needed — the two RLS-scoped queries reproduce the old handler.
40633
+ */
40615
40634
  async list(params) {
40616
- const qs = new URLSearchParams();
40617
- if (params?.namePrefix)
40618
- qs.set("namePrefix", params.namePrefix);
40619
- if (params?.industry)
40620
- qs.set("industry", params.industry);
40621
- if (params?.limit != null)
40622
- qs.set("limit", String(params.limit));
40623
- const query = qs.toString();
40624
- const path = query ? `/api/v1/templates?${query}` : "/api/v1/templates";
40625
- const res = await this.client.get(path);
40626
- return res.data;
40635
+ const { data: sitesData, error: sitesError } = await this.client.db.from("sites").select("id, handle, name");
40636
+ if (sitesError)
40637
+ throw new NeopressApiError("DB_ERROR", sitesError.message, 0, sitesError);
40638
+ const siteMeta = /* @__PURE__ */ new Map();
40639
+ for (const site of sitesData ?? []) {
40640
+ if (typeof site.id !== "number")
40641
+ continue;
40642
+ siteMeta.set(site.id, {
40643
+ handle: site.handle ?? null,
40644
+ name: site.name ?? null
40645
+ });
40646
+ }
40647
+ const siteIds = [...siteMeta.keys()];
40648
+ if (siteIds.length === 0)
40649
+ return [];
40650
+ const namePrefix = params?.namePrefix?.trim();
40651
+ const industry = params?.industry?.trim();
40652
+ const limit = Math.min(Math.max(params?.limit ?? 100, 1), 500);
40653
+ let query = this.client.db.from("templates").select("id, name, industry, reference_url, preview_img_urls, description, created_at").in("id", siteIds);
40654
+ if (namePrefix)
40655
+ query = query.ilike("name", `${namePrefix}%`);
40656
+ if (industry)
40657
+ query = query.eq("industry", industry);
40658
+ if (params?.order)
40659
+ query = query.order("created_at", { ascending: params.order === "asc" });
40660
+ query = query.limit(limit);
40661
+ const { data, error: error48 } = await query;
40662
+ if (error48)
40663
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40664
+ return (data ?? []).map((t) => {
40665
+ const siteId = t.id;
40666
+ return {
40667
+ siteId,
40668
+ handle: siteMeta.get(siteId)?.handle ?? null,
40669
+ siteName: siteMeta.get(siteId)?.name ?? null,
40670
+ name: t.name,
40671
+ industry: t.industry,
40672
+ referenceUrl: t.reference_url,
40673
+ previewImgUrls: t.preview_img_urls ?? null,
40674
+ description: t.description ?? null,
40675
+ createdAt: t.created_at
40676
+ };
40677
+ });
40627
40678
  }
40628
40679
  };
40629
40680
 
@@ -40646,7 +40697,12 @@ var HeadshotsEndpoint = class {
40646
40697
  query = query.eq("attire", params.attire);
40647
40698
  if (params?.isActive !== void 0)
40648
40699
  query = query.eq("is_active", params.isActive);
40649
- const { data, error: error48, count } = await query.order("age_band", { ascending: true }).order("gender", { ascending: true }).order("ethnicity", { ascending: true }).order("variation_index", { ascending: true }).range(offset, offset + limit - 1);
40700
+ if (params?.order) {
40701
+ query = query.order("created_at", { ascending: params.order === "asc" });
40702
+ } else {
40703
+ query = query.order("age_band", { ascending: true }).order("gender", { ascending: true }).order("ethnicity", { ascending: true }).order("variation_index", { ascending: true });
40704
+ }
40705
+ const { data, error: error48, count } = await query.range(offset, offset + limit - 1);
40650
40706
  if (error48)
40651
40707
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40652
40708
  return {
@@ -40912,6 +40968,16 @@ async function getGlobalClientAsync() {
40912
40968
  return new NeopressClient({ accessToken: token, siteId: 0, baseUrl: getBaseUrl(), supabaseUrl: sb.url, supabaseAnonKey: sb.anonKey });
40913
40969
  }
40914
40970
 
40971
+ // src/utils/order.ts
40972
+ function parseOrder(value) {
40973
+ if (value === void 0) return void 0;
40974
+ if (value !== "asc" && value !== "desc") {
40975
+ fail(`Invalid --order value "${value}". Use "asc" or "desc".`);
40976
+ }
40977
+ return value;
40978
+ }
40979
+ var ORDER_OPTION_DESC = "Sort by created_at (asc or desc)";
40980
+
40915
40981
  // src/commands/sites.ts
40916
40982
  function registerSiteCommands(program3) {
40917
40983
  const sites = program3.command("sites").description("Manage sites").addHelpText("after", `
@@ -40922,9 +40988,9 @@ Examples:
40922
40988
  $ neopress sites use my-site
40923
40989
  $ neopress sites current
40924
40990
  Run \`sites use <id|handle>\` to set the global active site, or \`sites create --pin\` to scope a site to the current directory. \`sites current\` shows which site commands will target and why.`);
40925
- sites.command("list").description("List accessible sites").action(async () => {
40991
+ sites.command("list").description("List accessible sites").option("--order <asc|desc>", ORDER_OPTION_DESC).action(async (options) => {
40926
40992
  const client = await getGlobalClientAsync();
40927
- const sitesList = await client.site.list();
40993
+ const sitesList = await client.site.list({ order: parseOrder(options.order) });
40928
40994
  output(sitesList);
40929
40995
  });
40930
40996
  sites.command("use <siteIdOrHandle>").description("Set active site (ID or handle)").addHelpText("after", `
@@ -41078,10 +41144,11 @@ Examples:
41078
41144
  $ neopress pages compile <pageId>
41079
41145
 
41080
41146
  Draft content fields (--tsx/--i18n/--draft-meta) need \`neopress publish\` to go live. \`pages delete\` is a soft delete.`);
41081
- pages.command("list").description("List pages").option("--status <status>", "Filter by status (draft/published)").option("--page <n>", "Page number", "1").option("--limit <n>", "Results per page", "20").action(async (options) => {
41147
+ pages.command("list").description("List pages").option("--status <status>", "Filter by status (draft/published)").option("--order <asc|desc>", ORDER_OPTION_DESC).option("--page <n>", "Page number", "1").option("--limit <n>", "Results per page", "20").action(async (options) => {
41082
41148
  const client = await getClientAsync();
41083
41149
  const res = await client.pages.list({
41084
41150
  status: options.status,
41151
+ order: parseOrder(options.order),
41085
41152
  page: parseInt(options.page, 10),
41086
41153
  limit: parseInt(options.limit, 10)
41087
41154
  });
@@ -41178,9 +41245,9 @@ Examples:
41178
41245
  $ neopress collections update 12 --schema @schema.json
41179
41246
 
41180
41247
  Collections are identified by numeric id, never name. delete is a soft delete \u2014 the collection is hidden from list/get but its row is retained.`);
41181
- collections.command("list").description("List collections").action(async () => {
41248
+ collections.command("list").description("List collections").option("--order <asc|desc>", ORDER_OPTION_DESC).action(async (options) => {
41182
41249
  const client = await getClientAsync();
41183
- const data = await client.collections.list();
41250
+ const data = await client.collections.list({ order: parseOrder(options.order) });
41184
41251
  output(data);
41185
41252
  });
41186
41253
  collections.command("get <collectionId>").description("Get collection details").action(async (collectionId) => {
@@ -41238,11 +41305,12 @@ Examples:
41238
41305
  $ neopress entries publish <entryId>
41239
41306
 
41240
41307
  Entries are created as drafts \u2014 run \`neopress entries publish <entryId>\` to make them live. create/update/delete revalidate the reader cache; delete is a soft delete.`);
41241
- entries.command("list").description("List entries in a collection").requiredOption("--collection <id>", "Collection ID").option("--status <status>", "Filter by status").option("--locale <locale>", "Filter by locale").option("--page <n>", "Page number", "1").option("--limit <n>", "Results per page", "20").action(async (options) => {
41308
+ entries.command("list").description("List entries in a collection").requiredOption("--collection <id>", "Collection ID").option("--status <status>", "Filter by status").option("--locale <locale>", "Filter by locale").option("--order <asc|desc>", ORDER_OPTION_DESC).option("--page <n>", "Page number", "1").option("--limit <n>", "Results per page", "20").action(async (options) => {
41242
41309
  const client = await getClientAsync();
41243
41310
  const res = await client.entries.list(parseInt(options.collection, 10), {
41244
41311
  status: options.status,
41245
41312
  locale: options.locale,
41313
+ order: parseOrder(options.order),
41246
41314
  page: parseInt(options.page, 10),
41247
41315
  limit: parseInt(options.limit, 10)
41248
41316
  });
@@ -41332,9 +41400,9 @@ Examples:
41332
41400
  $ neopress forms publish <formId>
41333
41401
  $ neopress forms responses <formId> --limit 50
41334
41402
  Forms are created as drafts \u2014 run \`neopress forms publish <formId>\` to make them live. JSON options take inline JSON or @file.`);
41335
- forms.command("list").description("List forms").action(async () => {
41403
+ forms.command("list").description("List forms").option("--order <asc|desc>", ORDER_OPTION_DESC).action(async (options) => {
41336
41404
  const client = await getClientAsync();
41337
- const data = await client.forms.list();
41405
+ const data = await client.forms.list({ order: parseOrder(options.order) });
41338
41406
  output(data);
41339
41407
  });
41340
41408
  forms.command("get <formId>").description("Get form details").action(async (formId) => {
@@ -41414,9 +41482,10 @@ Examples:
41414
41482
  $ neopress assets upload ./hero.png --folder images
41415
41483
  $ neopress assets list --limit 50
41416
41484
  Upload runs presign \u2192 PUT to storage \u2192 register in one step; content type is inferred from the file extension.`);
41417
- assets.command("list").description("List assets").option("--page <n>", "Page number", "1").option("--limit <n>", "Results per page", "20").action(async (options) => {
41485
+ assets.command("list").description("List assets").option("--order <asc|desc>", ORDER_OPTION_DESC).option("--page <n>", "Page number", "1").option("--limit <n>", "Results per page", "20").action(async (options) => {
41418
41486
  const client = await getClientAsync();
41419
41487
  const res = await client.assets.list({
41488
+ order: parseOrder(options.order),
41420
41489
  page: parseInt(options.page, 10),
41421
41490
  limit: parseInt(options.limit, 10)
41422
41491
  });
@@ -41499,9 +41568,9 @@ Examples:
41499
41568
  $ neopress redirects create --source /docs --dest /guide --type 308
41500
41569
  $ neopress redirects list
41501
41570
  Rules are created enabled. delete <ruleId> removes a rule permanently.`);
41502
- redirects.command("list").description("List redirect rules").action(async () => {
41571
+ redirects.command("list").description("List redirect rules").option("--order <asc|desc>", `${ORDER_OPTION_DESC} (default sort: priority)`).action(async (options) => {
41503
41572
  const client = await getClientAsync();
41504
- const data = await client.redirects.list();
41573
+ const data = await client.redirects.list({ order: parseOrder(options.order) });
41505
41574
  output(data);
41506
41575
  });
41507
41576
  redirects.command("create").description("Create a redirect rule").addHelpText("after", `
@@ -41564,11 +41633,12 @@ Examples:
41564
41633
  $ neopress entry-references list --from-entry 12
41565
41634
  $ neopress entry-references delete <referenceId>
41566
41635
  --field is a reference field key from the source entry's collection schema. delete is a hard delete and removes the relation immediately.`);
41567
- refs.command("list").description("List entry references").option("--from-entry <id>", "Filter by source entry ID").option("--to-entry <id>", "Filter by target entry ID").action(async (options) => {
41636
+ refs.command("list").description("List entry references").option("--from-entry <id>", "Filter by source entry ID").option("--to-entry <id>", "Filter by target entry ID").option("--order <asc|desc>", ORDER_OPTION_DESC).action(async (options) => {
41568
41637
  const client = await getClientAsync();
41569
41638
  const data = await client.entryReferences.list({
41570
41639
  fromEntryId: options.fromEntry ? parseInt(options.fromEntry, 10) : void 0,
41571
- toEntryId: options.toEntry ? parseInt(options.toEntry, 10) : void 0
41640
+ toEntryId: options.toEntry ? parseInt(options.toEntry, 10) : void 0,
41641
+ order: parseOrder(options.order)
41572
41642
  });
41573
41643
  output(data);
41574
41644
  });
@@ -41730,15 +41800,12 @@ Examples:
41730
41800
  $ neopress templates list --industry saas --limit 50
41731
41801
  $ neopress templates delete
41732
41802
  \`templates mark\` operates on the active site. \`templates delete\` permanently removes the template metadata AND the connected site \u2014 it is destructive and not recoverable.`);
41733
- templates.command("list").description("List templates across sites you can access").option("--name-prefix <prefix>", "Filter by template name prefix").option("--industry <industry>", "Filter by industry key").option("--limit <n>", "Max results (default 100, max 500)").action(async (options) => {
41734
- const token = process.env.NEOPRESS_ACCESS_TOKEN || await getValidAccessToken() || void 0;
41735
- if (!token) {
41736
- fail("Not authenticated. Run `neopress login`.");
41737
- }
41738
- const client = new NeopressClient({ accessToken: token, siteId: 0, baseUrl: getBaseUrl() });
41803
+ templates.command("list").description("List templates across sites you can access").option("--name-prefix <prefix>", "Filter by template name prefix").option("--industry <industry>", "Filter by industry key").option("--order <asc|desc>", ORDER_OPTION_DESC).option("--limit <n>", "Max results (default 100, max 500)").action(async (options) => {
41804
+ const client = await getGlobalClientAsync();
41739
41805
  const templates2 = await client.templates.list({
41740
41806
  namePrefix: options.namePrefix,
41741
41807
  industry: options.industry,
41808
+ order: parseOrder(options.order),
41742
41809
  limit: options.limit ? parseInt(options.limit, 10) : void 0
41743
41810
  });
41744
41811
  output(templates2);
@@ -41803,7 +41870,7 @@ Examples:
41803
41870
  $ neopress headshots create --image-url https://cdn/x.webp --age-band young --gender male --ethnicity white --variation-index 0
41804
41871
  $ neopress headshots update 12 --inactive
41805
41872
  The pool is global and RLS-gated: only the super-admin account can read or write it. No active site is needed.`);
41806
- headshots.command("list").description("List headshot pool entries").option("--age-band <band>", `Filter by age band (${AGE_BANDS.join("/")})`).option("--gender <gender>", `Filter by gender (${GENDERS.join("/")})`).option("--ethnicity <ethnicity>", `Filter by ethnicity (${ETHNICITIES.join("/")})`).option("--attire <attire>", `Filter by attire (${ATTIRES.join("/")})`).option("--active", "Only active entries").option("--inactive", "Only inactive entries").option("--page <n>", "Page number", "1").option("--limit <n>", "Results per page", "20").addHelpText("after", `
41873
+ headshots.command("list").description("List headshot pool entries").option("--age-band <band>", `Filter by age band (${AGE_BANDS.join("/")})`).option("--gender <gender>", `Filter by gender (${GENDERS.join("/")})`).option("--ethnicity <ethnicity>", `Filter by ethnicity (${ETHNICITIES.join("/")})`).option("--attire <attire>", `Filter by attire (${ATTIRES.join("/")})`).option("--active", "Only active entries").option("--inactive", "Only inactive entries").option("--order <asc|desc>", ORDER_OPTION_DESC).option("--page <n>", "Page number", "1").option("--limit <n>", "Results per page", "20").addHelpText("after", `
41807
41874
  Examples:
41808
41875
  $ neopress headshots list
41809
41876
  $ neopress headshots list --gender female --age-band middle --active
@@ -41818,6 +41885,7 @@ Filters are equality matches on the pool's demographic columns; results are pagi
41818
41885
  const params = {
41819
41886
  page: parseInt(options.page, 10),
41820
41887
  limit: parseInt(options.limit, 10),
41888
+ order: parseOrder(options.order),
41821
41889
  ageBand: options.ageBand,
41822
41890
  gender: options.gender,
41823
41891
  ethnicity: options.ethnicity,
@@ -41997,7 +42065,7 @@ function registerGuideCommands(program3) {
41997
42065
 
41998
42066
  // src/bin.ts
41999
42067
  var program2 = new Command();
42000
- program2.name("neopress").description("Neopress CLI \u2014 manage your site from the terminal").version("4.2.0").option("--json", "Output in JSON format (for AI tools and pipes)").option("--site <idOrHandle>", "Target a specific site (overrides active site; not persisted)").option("--no-input", "Non-interactive mode").hook("preAction", (thisCommand) => {
42068
+ program2.name("neopress").description("Neopress CLI \u2014 manage your site from the terminal").version("4.3.0").option("--json", "Output in JSON format (for AI tools and pipes)").option("--site <idOrHandle>", "Target a specific site (overrides active site; not persisted)").option("--no-input", "Non-interactive mode").hook("preAction", (thisCommand) => {
42001
42069
  const opts = thisCommand.optsWithGlobals();
42002
42070
  if (opts.site) setSiteOverride(String(opts.site));
42003
42071
  if (opts.json || !process.stdout.isTTY) setJsonMode(true);
package/dist/index.js CHANGED
@@ -21220,7 +21220,8 @@ var PagesEndpoint = class {
21220
21220
  }
21221
21221
  async list(params) {
21222
21222
  const { page, limit, offset } = this.client.resolvePagination(params);
21223
- let query = this.client.db.from("pages").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("path");
21223
+ let query = this.client.db.from("pages").select("*", { count: "exact" }).eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
21224
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("path");
21224
21225
  if (params?.status)
21225
21226
  query = query.eq("status", params.status);
21226
21227
  const { data, error: error48, count } = await query.range(offset, offset + limit - 1);
@@ -36274,8 +36275,10 @@ var CollectionsEndpoint = class {
36274
36275
  get basePath() {
36275
36276
  return `${this.client.siteBasePath}/collections`;
36276
36277
  }
36277
- async list() {
36278
- const { data, error: error48 } = await this.client.db.from("collections").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("name");
36278
+ async list(params) {
36279
+ let query = this.client.db.from("collections").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
36280
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("name");
36281
+ const { data, error: error48 } = await query;
36279
36282
  if (error48)
36280
36283
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
36281
36284
  return (data ?? []).map((r) => serializeCollection(r));
@@ -36348,7 +36351,7 @@ var EntriesEndpoint = class {
36348
36351
  }
36349
36352
  async list(collectionId, params) {
36350
36353
  const { page, limit, offset } = this.client.resolvePagination(params);
36351
- 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 });
36354
+ 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" });
36352
36355
  if (params?.status)
36353
36356
  query = query.eq("status", params.status);
36354
36357
  if (params?.locale)
@@ -36439,8 +36442,10 @@ var FormsEndpoint = class {
36439
36442
  get basePath() {
36440
36443
  return `${this.client.siteBasePath}/forms`;
36441
36444
  }
36442
- async list() {
36443
- const { data, error: error48 } = await this.client.db.from("forms").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null).order("name");
36445
+ async list(params) {
36446
+ let query = this.client.db.from("forms").select("*").eq("site_id", this.client.siteIdNumber).is("draft_deleted_at", null);
36447
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("name");
36448
+ const { data, error: error48 } = await query;
36444
36449
  if (error48)
36445
36450
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
36446
36451
  const rows = data ?? [];
@@ -36519,7 +36524,7 @@ var AssetsEndpoint = class {
36519
36524
  }
36520
36525
  async list(params) {
36521
36526
  const { page, limit, offset } = this.client.resolvePagination(params);
36522
- 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);
36527
+ 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);
36523
36528
  if (error48)
36524
36529
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
36525
36530
  const rows = data ?? [];
@@ -36585,7 +36590,7 @@ var SiteEndpoint = class {
36585
36590
  constructor(client) {
36586
36591
  this.client = client;
36587
36592
  }
36588
- async list() {
36593
+ async list(params) {
36589
36594
  const profileId = this.client.authUserId;
36590
36595
  if (!profileId) {
36591
36596
  throw new NeopressApiError("NO_AUTH", "site.list requires an authenticated access token", 0);
@@ -36593,10 +36598,15 @@ var SiteEndpoint = class {
36593
36598
  const { data, error: error48 } = await this.client.db.from("sites_profiles").select("role, sites!inner(*)").eq("profile_id", profileId).eq("status", "active");
36594
36599
  if (error48)
36595
36600
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
36596
- return (data ?? []).map((m) => ({
36601
+ const sites = (data ?? []).map((m) => ({
36597
36602
  ...serializeSite(m.sites),
36598
36603
  role: m.role
36599
36604
  }));
36605
+ if (params?.order) {
36606
+ const dir = params.order === "asc" ? 1 : -1;
36607
+ sites.sort((a, b) => (a.createdAt ?? "") < (b.createdAt ?? "") ? -dir : (a.createdAt ?? "") > (b.createdAt ?? "") ? dir : 0);
36608
+ }
36609
+ return sites;
36600
36610
  }
36601
36611
  async get() {
36602
36612
  const { data, error: error48 } = await this.client.db.from("sites").select("*").eq("id", this.client.siteIdNumber).single();
@@ -36634,8 +36644,10 @@ var RedirectsEndpoint = class {
36634
36644
  constructor(client) {
36635
36645
  this.client = client;
36636
36646
  }
36637
- async list() {
36638
- const { data, error: error48 } = await this.client.db.from("redirect_rules").select("*").eq("site_id", this.client.siteIdNumber).order("priority", { ascending: true });
36647
+ async list(params) {
36648
+ let query = this.client.db.from("redirect_rules").select("*").eq("site_id", this.client.siteIdNumber);
36649
+ query = params?.order ? query.order("created_at", { ascending: params.order === "asc" }) : query.order("priority", { ascending: true });
36650
+ const { data, error: error48 } = await query;
36639
36651
  if (error48)
36640
36652
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
36641
36653
  return (data ?? []).map((r) => serializeRedirect(r));
@@ -36712,7 +36724,7 @@ var EntryReferencesEndpoint = class {
36712
36724
  query = query.eq("from_entry_id", params.fromEntryId);
36713
36725
  if (params?.toEntryId)
36714
36726
  query = query.eq("to_entry_id", params.toEntryId);
36715
- const { data, error: error48 } = await query.order("created_at", { ascending: false });
36727
+ const { data, error: error48 } = await query.order("created_at", { ascending: params?.order === "asc" });
36716
36728
  if (error48)
36717
36729
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
36718
36730
  return (data ?? []).map((r) => serializeEntryReference(r));
@@ -36834,18 +36846,57 @@ var TemplatesEndpoint = class {
36834
36846
  const res = await this.client.delete(this.basePath);
36835
36847
  return res.data;
36836
36848
  }
36849
+ /**
36850
+ * List templates across the sites the caller can access.
36851
+ *
36852
+ * Direct-DB: `templates` is public-read and `sites` RLS scopes the visible set
36853
+ * (own active memberships, or all sites for the super admin), so no server
36854
+ * route is needed — the two RLS-scoped queries reproduce the old handler.
36855
+ */
36837
36856
  async list(params) {
36838
- const qs = new URLSearchParams();
36839
- if (params?.namePrefix)
36840
- qs.set("namePrefix", params.namePrefix);
36841
- if (params?.industry)
36842
- qs.set("industry", params.industry);
36843
- if (params?.limit != null)
36844
- qs.set("limit", String(params.limit));
36845
- const query = qs.toString();
36846
- const path = query ? `/api/v1/templates?${query}` : "/api/v1/templates";
36847
- const res = await this.client.get(path);
36848
- return res.data;
36857
+ const { data: sitesData, error: sitesError } = await this.client.db.from("sites").select("id, handle, name");
36858
+ if (sitesError)
36859
+ throw new NeopressApiError("DB_ERROR", sitesError.message, 0, sitesError);
36860
+ const siteMeta = /* @__PURE__ */ new Map();
36861
+ for (const site of sitesData ?? []) {
36862
+ if (typeof site.id !== "number")
36863
+ continue;
36864
+ siteMeta.set(site.id, {
36865
+ handle: site.handle ?? null,
36866
+ name: site.name ?? null
36867
+ });
36868
+ }
36869
+ const siteIds = [...siteMeta.keys()];
36870
+ if (siteIds.length === 0)
36871
+ return [];
36872
+ const namePrefix = params?.namePrefix?.trim();
36873
+ const industry = params?.industry?.trim();
36874
+ const limit = Math.min(Math.max(params?.limit ?? 100, 1), 500);
36875
+ let query = this.client.db.from("templates").select("id, name, industry, reference_url, preview_img_urls, description, created_at").in("id", siteIds);
36876
+ if (namePrefix)
36877
+ query = query.ilike("name", `${namePrefix}%`);
36878
+ if (industry)
36879
+ query = query.eq("industry", industry);
36880
+ if (params?.order)
36881
+ query = query.order("created_at", { ascending: params.order === "asc" });
36882
+ query = query.limit(limit);
36883
+ const { data, error: error48 } = await query;
36884
+ if (error48)
36885
+ throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
36886
+ return (data ?? []).map((t) => {
36887
+ const siteId = t.id;
36888
+ return {
36889
+ siteId,
36890
+ handle: siteMeta.get(siteId)?.handle ?? null,
36891
+ siteName: siteMeta.get(siteId)?.name ?? null,
36892
+ name: t.name,
36893
+ industry: t.industry,
36894
+ referenceUrl: t.reference_url,
36895
+ previewImgUrls: t.preview_img_urls ?? null,
36896
+ description: t.description ?? null,
36897
+ createdAt: t.created_at
36898
+ };
36899
+ });
36849
36900
  }
36850
36901
  };
36851
36902
 
@@ -36868,7 +36919,12 @@ var HeadshotsEndpoint = class {
36868
36919
  query = query.eq("attire", params.attire);
36869
36920
  if (params?.isActive !== void 0)
36870
36921
  query = query.eq("is_active", params.isActive);
36871
- const { data, error: error48, count } = await query.order("age_band", { ascending: true }).order("gender", { ascending: true }).order("ethnicity", { ascending: true }).order("variation_index", { ascending: true }).range(offset, offset + limit - 1);
36922
+ if (params?.order) {
36923
+ query = query.order("created_at", { ascending: params.order === "asc" });
36924
+ } else {
36925
+ query = query.order("age_band", { ascending: true }).order("gender", { ascending: true }).order("ethnicity", { ascending: true }).order("variation_index", { ascending: true });
36926
+ }
36927
+ const { data, error: error48, count } = await query.range(offset, offset + limit - 1);
36872
36928
  if (error48)
36873
36929
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
36874
36930
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neopress/cli",
3
- "version": "4.2.0",
3
+ "version": "4.3.0",
4
4
  "description": "Neopress CLI — manage your Neopress site from the terminal",
5
5
  "type": "module",
6
6
  "keywords": [