@neopress/cli 4.1.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 +222 -102
  2. package/dist/index.js +157 -68
  3. package/package.json +1 -1
package/dist/bin.cjs CHANGED
@@ -3387,6 +3387,7 @@ function fail(message, code = 1) {
3387
3387
  // src/utils/config.ts
3388
3388
  var import_node_fs2 = require("fs");
3389
3389
  var import_node_path2 = require("path");
3390
+ var import_node_os2 = require("os");
3390
3391
 
3391
3392
  // src/utils/token-store.ts
3392
3393
  var import_node_fs = require("fs");
@@ -3421,31 +3422,55 @@ function clearSession() {
3421
3422
  // src/utils/config.ts
3422
3423
  var PROJECT_CONFIG = ".neopressrc.json";
3423
3424
  var WORKSPACE_CONFIG = ".neopress/config.json";
3424
- function readProjectConfig() {
3425
- let config2 = {};
3426
- for (const file2 of [PROJECT_CONFIG, WORKSPACE_CONFIG]) {
3427
- try {
3428
- const parsed = JSON.parse((0, import_node_fs2.readFileSync)(file2, "utf-8"));
3429
- config2 = {
3430
- ...config2,
3431
- ...typeof parsed.siteId === "number" ? { siteId: parsed.siteId } : {},
3432
- ...typeof parsed.baseUrl === "string" ? { baseUrl: parsed.baseUrl } : {}
3433
- };
3434
- } catch {
3425
+ function readConfigFileAt(path) {
3426
+ try {
3427
+ const parsed = JSON.parse((0, import_node_fs2.readFileSync)(path, "utf-8"));
3428
+ return {
3429
+ ...typeof parsed.siteId === "number" ? { siteId: parsed.siteId } : {},
3430
+ ...typeof parsed.baseUrl === "string" ? { baseUrl: parsed.baseUrl } : {}
3431
+ };
3432
+ } catch {
3433
+ return {};
3434
+ }
3435
+ }
3436
+ function locateProjectConfig(startDir = process.cwd()) {
3437
+ const home = (0, import_node_path2.resolve)((0, import_node_os2.homedir)());
3438
+ let dir = (0, import_node_path2.resolve)(startDir);
3439
+ while (true) {
3440
+ let merged = {};
3441
+ let foundPath;
3442
+ for (const rel of [PROJECT_CONFIG, WORKSPACE_CONFIG]) {
3443
+ const p = (0, import_node_path2.join)(dir, rel);
3444
+ const cfg = readConfigFileAt(p);
3445
+ if (Object.keys(cfg).length > 0) {
3446
+ merged = { ...merged, ...cfg };
3447
+ if (!foundPath) foundPath = p;
3448
+ }
3435
3449
  }
3450
+ if (Object.keys(merged).length > 0) return { config: merged, path: foundPath };
3451
+ const parent = (0, import_node_path2.dirname)(dir);
3452
+ if (dir === home || parent === dir) break;
3453
+ dir = parent;
3436
3454
  }
3437
- return config2;
3455
+ return { config: {} };
3456
+ }
3457
+ function readProjectConfig() {
3458
+ return locateProjectConfig().config;
3438
3459
  }
3439
3460
  function writeWorkspaceConfig(next) {
3440
3461
  (0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(WORKSPACE_CONFIG), { recursive: true });
3441
3462
  (0, import_node_fs2.writeFileSync)(WORKSPACE_CONFIG, JSON.stringify(next, null, 2) + "\n", "utf-8");
3442
3463
  }
3443
- function getSiteId() {
3444
- const envSiteId = process.env.NEOPRESS_SITE_ID;
3445
- if (envSiteId) return parseInt(envSiteId, 10);
3446
- const projectConfig = readProjectConfig();
3447
- if (projectConfig.siteId) return projectConfig.siteId;
3448
- return loadSession().activeSiteId;
3464
+ function writeProjectSiteId(siteId) {
3465
+ let raw = {};
3466
+ try {
3467
+ const parsed = JSON.parse((0, import_node_fs2.readFileSync)(PROJECT_CONFIG, "utf-8"));
3468
+ if (parsed && typeof parsed === "object") raw = parsed;
3469
+ } catch {
3470
+ }
3471
+ const next = { ...raw, siteId };
3472
+ (0, import_node_fs2.writeFileSync)(PROJECT_CONFIG, JSON.stringify(next, null, 2) + "\n", "utf-8");
3473
+ return (0, import_node_path2.resolve)(PROJECT_CONFIG);
3449
3474
  }
3450
3475
  function getBaseUrl() {
3451
3476
  return process.env.NEOPRESS_BASE_URL || readProjectConfig().baseUrl || "https://app.neopress.ai";
@@ -3461,12 +3486,24 @@ var siteOverride;
3461
3486
  function setSiteOverride(value) {
3462
3487
  siteOverride = value;
3463
3488
  }
3464
- function getSiteTarget() {
3489
+ function resolveSiteTarget() {
3465
3490
  if (siteOverride) {
3466
3491
  const n = parseInt(siteOverride, 10);
3467
- return String(n) === siteOverride ? n : siteOverride;
3492
+ const value = String(n) === siteOverride ? n : siteOverride;
3493
+ return { value, source: "flag", detail: "--site" };
3494
+ }
3495
+ const envSiteId = process.env.NEOPRESS_SITE_ID;
3496
+ if (envSiteId) return { value: parseInt(envSiteId, 10), source: "env", detail: "NEOPRESS_SITE_ID" };
3497
+ const located = locateProjectConfig();
3498
+ if (typeof located.config.siteId === "number") {
3499
+ return { value: located.config.siteId, source: "project-config", detail: located.path };
3468
3500
  }
3469
- return getSiteId();
3501
+ const active = loadSession().activeSiteId;
3502
+ if (active) return { value: active, source: "global-active", detail: "sites use" };
3503
+ return { value: void 0, source: "none" };
3504
+ }
3505
+ function getSiteTarget() {
3506
+ return resolveSiteTarget().value;
3470
3507
  }
3471
3508
 
3472
3509
  // src/utils/pkce.ts
@@ -3759,11 +3796,11 @@ function __rest(s, e) {
3759
3796
  }
3760
3797
  function __awaiter(thisArg, _arguments, P, generator) {
3761
3798
  function adopt(value) {
3762
- return value instanceof P ? value : new P(function(resolve) {
3763
- resolve(value);
3799
+ return value instanceof P ? value : new P(function(resolve2) {
3800
+ resolve2(value);
3764
3801
  });
3765
3802
  }
3766
- return new (P || (P = Promise))(function(resolve, reject) {
3803
+ return new (P || (P = Promise))(function(resolve2, reject) {
3767
3804
  function fulfilled(value) {
3768
3805
  try {
3769
3806
  step(generator.next(value));
@@ -3779,7 +3816,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
3779
3816
  }
3780
3817
  }
3781
3818
  function step(result) {
3782
- result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
3819
+ result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
3783
3820
  }
3784
3821
  step((generator = generator.apply(thisArg, _arguments || [])).next());
3785
3822
  });
@@ -4150,18 +4187,18 @@ var PostgrestError = class extends Error {
4150
4187
  }
4151
4188
  };
4152
4189
  function sleep(ms, signal) {
4153
- return new Promise((resolve) => {
4190
+ return new Promise((resolve2) => {
4154
4191
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
4155
- resolve();
4192
+ resolve2();
4156
4193
  return;
4157
4194
  }
4158
4195
  const id = setTimeout(() => {
4159
4196
  signal === null || signal === void 0 || signal.removeEventListener("abort", onAbort);
4160
- resolve();
4197
+ resolve2();
4161
4198
  }, ms);
4162
4199
  function onAbort() {
4163
4200
  clearTimeout(id);
4164
- resolve();
4201
+ resolve2();
4165
4202
  }
4166
4203
  signal === null || signal === void 0 || signal.addEventListener("abort", onAbort);
4167
4204
  });
@@ -12110,15 +12147,15 @@ var RealtimeChannel = class _RealtimeChannel {
12110
12147
  }
12111
12148
  }
12112
12149
  } else {
12113
- return new Promise((resolve) => {
12150
+ return new Promise((resolve2) => {
12114
12151
  var _a3, _b2, _c;
12115
12152
  const push = this.channelAdapter.push(args.type, args, opts.timeout || this.timeout);
12116
12153
  if (args.type === "broadcast" && !((_c = (_b2 = (_a3 = this.params) === null || _a3 === void 0 ? void 0 : _a3.config) === null || _b2 === void 0 ? void 0 : _b2.broadcast) === null || _c === void 0 ? void 0 : _c.ack)) {
12117
- resolve("ok");
12154
+ resolve2("ok");
12118
12155
  }
12119
- push.receive("ok", () => resolve("ok"));
12120
- push.receive("error", () => resolve("error"));
12121
- push.receive("timeout", () => resolve("timed out"));
12156
+ push.receive("ok", () => resolve2("ok"));
12157
+ push.receive("error", () => resolve2("error"));
12158
+ push.receive("timeout", () => resolve2("timed out"));
12122
12159
  });
12123
12160
  }
12124
12161
  }
@@ -12143,8 +12180,8 @@ var RealtimeChannel = class _RealtimeChannel {
12143
12180
  * @category Realtime
12144
12181
  */
12145
12182
  async unsubscribe(timeout = this.timeout) {
12146
- return new Promise((resolve) => {
12147
- this.channelAdapter.unsubscribe(timeout).receive("ok", () => resolve("ok")).receive("timeout", () => resolve("timed out")).receive("error", () => resolve("error"));
12183
+ return new Promise((resolve2) => {
12184
+ this.channelAdapter.unsubscribe(timeout).receive("ok", () => resolve2("ok")).receive("timeout", () => resolve2("timed out")).receive("error", () => resolve2("error"));
12148
12185
  });
12149
12186
  }
12150
12187
  /**
@@ -12225,8 +12262,8 @@ var RealtimeChannel = class _RealtimeChannel {
12225
12262
  }
12226
12263
  /** @internal */
12227
12264
  _notThisChannelEvent(event, ref) {
12228
- const { close, error: error48, leave, join: join2 } = CHANNEL_EVENTS;
12229
- const events = [close, error48, leave, join2];
12265
+ const { close, error: error48, leave, join: join3 } = CHANNEL_EVENTS;
12266
+ const events = [close, error48, leave, join3];
12230
12267
  return ref && events.includes(event) && ref !== this.joinPush.ref;
12231
12268
  }
12232
12269
  /** @internal */
@@ -12339,11 +12376,11 @@ var SocketAdapter = class {
12339
12376
  this.socket.connect();
12340
12377
  }
12341
12378
  disconnect(callback, code, reason, timeout = 1e4) {
12342
- return new Promise((resolve) => {
12343
- setTimeout(() => resolve("timeout"), timeout);
12379
+ return new Promise((resolve2) => {
12380
+ setTimeout(() => resolve2("timeout"), timeout);
12344
12381
  this.socket.disconnect(() => {
12345
12382
  callback();
12346
- resolve("ok");
12383
+ resolve2("ok");
12347
12384
  }, code, reason);
12348
12385
  });
12349
12386
  }
@@ -13757,7 +13794,7 @@ var _getRequestParams = (method, options, parameters, body) => {
13757
13794
  return _objectSpread22(_objectSpread22({}, params), parameters);
13758
13795
  };
13759
13796
  async function _handleRequest(fetcher, method, url2, options, parameters, body, namespace) {
13760
- return new Promise((resolve, reject) => {
13797
+ return new Promise((resolve2, reject) => {
13761
13798
  fetcher(url2, _getRequestParams(method, options, parameters, body)).then((result) => {
13762
13799
  if (!result.ok) throw result;
13763
13800
  if (options === null || options === void 0 ? void 0 : options.noResolveJson) return result;
@@ -13767,7 +13804,7 @@ async function _handleRequest(fetcher, method, url2, options, parameters, body,
13767
13804
  if (!contentType || !contentType.includes("application/json")) return {};
13768
13805
  }
13769
13806
  return result.json();
13770
- }).then((data) => resolve(data)).catch((error48) => handleError(error48, reject, options, namespace));
13807
+ }).then((data) => resolve2(data)).catch((error48) => handleError(error48, reject, options, namespace));
13771
13808
  });
13772
13809
  }
13773
13810
  function createFetchApi(namespace = "storage") {
@@ -24012,11 +24049,11 @@ var DEFAULT_TRACE_PROPAGATION_OPTIONS = {
24012
24049
  };
24013
24050
  function __awaiter2(thisArg, _arguments, P, generator) {
24014
24051
  function adopt(value) {
24015
- return value instanceof P ? value : new P(function(resolve) {
24016
- resolve(value);
24052
+ return value instanceof P ? value : new P(function(resolve2) {
24053
+ resolve2(value);
24017
24054
  });
24018
24055
  }
24019
- return new (P || (P = Promise))(function(resolve, reject) {
24056
+ return new (P || (P = Promise))(function(resolve2, reject) {
24020
24057
  function fulfilled(value) {
24021
24058
  try {
24022
24059
  step(generator.next(value));
@@ -24032,7 +24069,7 @@ function __awaiter2(thisArg, _arguments, P, generator) {
24032
24069
  }
24033
24070
  }
24034
24071
  function step(result) {
24035
- result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
24072
+ result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
24036
24073
  }
24037
24074
  step((generator = generator.apply(thisArg, _arguments || [])).next());
24038
24075
  });
@@ -24961,7 +24998,8 @@ var PagesEndpoint = class {
24961
24998
  }
24962
24999
  async list(params) {
24963
25000
  const { page, limit, offset } = this.client.resolvePagination(params);
24964
- 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");
24965
25003
  if (params?.status)
24966
25004
  query = query.eq("status", params.status);
24967
25005
  const { data, error: error48, count } = await query.range(offset, offset + limit - 1);
@@ -39254,8 +39292,8 @@ var SiteHistoryService = class _SiteHistoryService {
39254
39292
  const previousLock = _SiteHistoryService.sessionLocks.get(key) ?? Promise.resolve();
39255
39293
  let releaseCurrentLock = () => {
39256
39294
  };
39257
- const currentLock = new Promise((resolve) => {
39258
- releaseCurrentLock = resolve;
39295
+ const currentLock = new Promise((resolve2) => {
39296
+ releaseCurrentLock = resolve2;
39259
39297
  });
39260
39298
  const queuedLock = previousLock.catch(() => {
39261
39299
  }).then(() => currentLock);
@@ -40015,8 +40053,10 @@ var CollectionsEndpoint = class {
40015
40053
  get basePath() {
40016
40054
  return `${this.client.siteBasePath}/collections`;
40017
40055
  }
40018
- async list() {
40019
- 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;
40020
40060
  if (error48)
40021
40061
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40022
40062
  return (data ?? []).map((r) => serializeCollection(r));
@@ -40089,7 +40129,7 @@ var EntriesEndpoint = class {
40089
40129
  }
40090
40130
  async list(collectionId, params) {
40091
40131
  const { page, limit, offset } = this.client.resolvePagination(params);
40092
- 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" });
40093
40133
  if (params?.status)
40094
40134
  query = query.eq("status", params.status);
40095
40135
  if (params?.locale)
@@ -40180,8 +40220,10 @@ var FormsEndpoint = class {
40180
40220
  get basePath() {
40181
40221
  return `${this.client.siteBasePath}/forms`;
40182
40222
  }
40183
- async list() {
40184
- 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;
40185
40227
  if (error48)
40186
40228
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40187
40229
  const rows = data ?? [];
@@ -40260,7 +40302,7 @@ var AssetsEndpoint = class {
40260
40302
  }
40261
40303
  async list(params) {
40262
40304
  const { page, limit, offset } = this.client.resolvePagination(params);
40263
- 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);
40264
40306
  if (error48)
40265
40307
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40266
40308
  const rows = data ?? [];
@@ -40326,7 +40368,7 @@ var SiteEndpoint = class {
40326
40368
  constructor(client) {
40327
40369
  this.client = client;
40328
40370
  }
40329
- async list() {
40371
+ async list(params) {
40330
40372
  const profileId = this.client.authUserId;
40331
40373
  if (!profileId) {
40332
40374
  throw new NeopressApiError("NO_AUTH", "site.list requires an authenticated access token", 0);
@@ -40334,10 +40376,15 @@ var SiteEndpoint = class {
40334
40376
  const { data, error: error48 } = await this.client.db.from("sites_profiles").select("role, sites!inner(*)").eq("profile_id", profileId).eq("status", "active");
40335
40377
  if (error48)
40336
40378
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40337
- return (data ?? []).map((m) => ({
40379
+ const sites = (data ?? []).map((m) => ({
40338
40380
  ...serializeSite(m.sites),
40339
40381
  role: m.role
40340
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;
40341
40388
  }
40342
40389
  async get() {
40343
40390
  const { data, error: error48 } = await this.client.db.from("sites").select("*").eq("id", this.client.siteIdNumber).single();
@@ -40375,8 +40422,10 @@ var RedirectsEndpoint = class {
40375
40422
  constructor(client) {
40376
40423
  this.client = client;
40377
40424
  }
40378
- async list() {
40379
- 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;
40380
40429
  if (error48)
40381
40430
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40382
40431
  return (data ?? []).map((r) => serializeRedirect(r));
@@ -40453,7 +40502,7 @@ var EntryReferencesEndpoint = class {
40453
40502
  query = query.eq("from_entry_id", params.fromEntryId);
40454
40503
  if (params?.toEntryId)
40455
40504
  query = query.eq("to_entry_id", params.toEntryId);
40456
- 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" });
40457
40506
  if (error48)
40458
40507
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40459
40508
  return (data ?? []).map((r) => serializeEntryReference(r));
@@ -40575,18 +40624,57 @@ var TemplatesEndpoint = class {
40575
40624
  const res = await this.client.delete(this.basePath);
40576
40625
  return res.data;
40577
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
+ */
40578
40634
  async list(params) {
40579
- const qs = new URLSearchParams();
40580
- if (params?.namePrefix)
40581
- qs.set("namePrefix", params.namePrefix);
40582
- if (params?.industry)
40583
- qs.set("industry", params.industry);
40584
- if (params?.limit != null)
40585
- qs.set("limit", String(params.limit));
40586
- const query = qs.toString();
40587
- const path = query ? `/api/v1/templates?${query}` : "/api/v1/templates";
40588
- const res = await this.client.get(path);
40589
- 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
+ });
40590
40678
  }
40591
40679
  };
40592
40680
 
@@ -40609,7 +40697,12 @@ var HeadshotsEndpoint = class {
40609
40697
  query = query.eq("attire", params.attire);
40610
40698
  if (params?.isActive !== void 0)
40611
40699
  query = query.eq("is_active", params.isActive);
40612
- 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);
40613
40706
  if (error48)
40614
40707
  throw new NeopressApiError("DB_ERROR", error48.message, 0, error48);
40615
40708
  return {
@@ -40875,17 +40968,29 @@ async function getGlobalClientAsync() {
40875
40968
  return new NeopressClient({ accessToken: token, siteId: 0, baseUrl: getBaseUrl(), supabaseUrl: sb.url, supabaseAnonKey: sb.anonKey });
40876
40969
  }
40877
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
+
40878
40981
  // src/commands/sites.ts
40879
40982
  function registerSiteCommands(program3) {
40880
40983
  const sites = program3.command("sites").description("Manage sites").addHelpText("after", `
40881
40984
  Examples:
40882
40985
  $ neopress sites list
40883
40986
  $ neopress sites create --name "My Site" --handle my-site --use
40987
+ $ neopress sites create --name "My Site" --handle my-site --pin
40884
40988
  $ neopress sites use my-site
40885
- Run \`sites use <id|handle>\` to set the active site for all other commands.`);
40886
- sites.command("list").description("List accessible sites").action(async () => {
40989
+ $ neopress sites current
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.`);
40991
+ sites.command("list").description("List accessible sites").option("--order <asc|desc>", ORDER_OPTION_DESC).action(async (options) => {
40887
40992
  const client = await getGlobalClientAsync();
40888
- const sitesList = await client.site.list();
40993
+ const sitesList = await client.site.list({ order: parseOrder(options.order) });
40889
40994
  output(sitesList);
40890
40995
  });
40891
40996
  sites.command("use <siteIdOrHandle>").description("Set active site (ID or handle)").addHelpText("after", `
@@ -40913,10 +41018,14 @@ Examples:
40913
41018
  $ neopress sites create --name "My Site" --handle my-site
40914
41019
  $ neopress sites create --prompt "a portfolio for a freelance photographer" --use
40915
41020
  $ neopress sites create --name Acme --handle acme --language en
40916
- Pass --prompt to auto-generate name/handle, or supply both --name and --handle. --language sets the default content locale (default en). --use sets the new site as active.`).option("--name <name>", "Site name").option("--handle <handle>", "Site handle").option("--prompt <prompt>", "Prompt to generate site name/handle").option("--language <locale>", "Default content locale", "en").option("--use", "Set the created site as active").action(async (options) => {
41021
+ Pass --prompt to auto-generate name/handle, or supply both --name and --handle. --language sets the default content locale (default en).
41022
+ --use sets the new site as the GLOBAL active site (shared across all directories). --pin instead writes ./.neopressrc.json for THIS directory only, leaving the global active site untouched \u2014 prefer it for parallel/automated runs so concurrent builds don't stomp each other's active site.`).option("--name <name>", "Site name").option("--handle <handle>", "Site handle").option("--prompt <prompt>", "Prompt to generate site name/handle").option("--language <locale>", "Default content locale", "en").option("--use", "Set the created site as the global active site").option("--pin", "Pin the created site to this directory via ./.neopressrc.json (no global state)").action(async (options) => {
40917
41023
  if (!options.prompt && !(options.name && options.handle)) {
40918
41024
  fail("Provide --prompt or both --name and --handle.");
40919
41025
  }
41026
+ if (options.use && options.pin) {
41027
+ fail("Pass only one of --use (global active site) or --pin (this directory only).");
41028
+ }
40920
41029
  const client = await getGlobalClientAsync();
40921
41030
  const site = await client.site.create({
40922
41031
  siteName: options.name,
@@ -40924,17 +41033,26 @@ Pass --prompt to auto-generate name/handle, or supply both --name and --handle.
40924
41033
  prompt: options.prompt,
40925
41034
  language: options.language
40926
41035
  });
40927
- if (options.use) {
40928
- setActiveSiteId(site.id);
40929
- }
41036
+ if (options.use) setActiveSiteId(site.id);
41037
+ let pinnedPath;
41038
+ if (options.pin) pinnedPath = writeProjectSiteId(site.id);
40930
41039
  output(site);
40931
- if (options.use && !isJsonMode()) ok(`Active site set to ${site.id}`);
41040
+ if (!isJsonMode()) {
41041
+ if (options.use) ok(`Active site set to ${site.id}`);
41042
+ if (options.pin) ok(`Pinned site ${site.id} for this directory (${pinnedPath})`);
41043
+ }
40932
41044
  });
40933
41045
  sites.command("get").description("Get current site info").action(async () => {
40934
41046
  const client = await getClientAsync();
40935
41047
  const site = await client.site.get();
40936
41048
  output(site);
40937
41049
  });
41050
+ sites.command("current").description("Show which site commands will target, and where it resolved from").addHelpText("after", `
41051
+ Resolution order: --site flag > NEOPRESS_SITE_ID > .neopressrc.json/.neopress/config.json (cwd and up) > global active site (set by \`sites use\`).
41052
+ Local check only \u2014 it does not contact the server. Unlike \`whoami\` (which always reports the global active site), this reports the site your next command will actually hit.`).action(() => {
41053
+ const { value, source, detail } = resolveSiteTarget();
41054
+ output({ siteId: value ?? null, source, resolvedFrom: detail ?? null });
41055
+ });
40938
41056
  sites.command("update").description("Update site settings").addHelpText("after", `
40939
41057
  Examples:
40940
41058
  $ neopress sites update --name "New Name"
@@ -40969,9 +41087,9 @@ Permanently deletes the site and all its content (workspace admin only) \u2014 t
40969
41087
  const { createInterface } = await import("readline");
40970
41088
  const rl = createInterface({ input: process.stdin, output: process.stdout });
40971
41089
  const answer = await new Promise(
40972
- (resolve) => rl.question(`Delete site "${siteIdOrHandle}" and ALL its content? Type the id/handle to confirm: `, (a) => {
41090
+ (resolve2) => rl.question(`Delete site "${siteIdOrHandle}" and ALL its content? Type the id/handle to confirm: `, (a) => {
40973
41091
  rl.close();
40974
- resolve(a.trim());
41092
+ resolve2(a.trim());
40975
41093
  })
40976
41094
  );
40977
41095
  if (answer !== siteIdOrHandle) {
@@ -41026,10 +41144,11 @@ Examples:
41026
41144
  $ neopress pages compile <pageId>
41027
41145
 
41028
41146
  Draft content fields (--tsx/--i18n/--draft-meta) need \`neopress publish\` to go live. \`pages delete\` is a soft delete.`);
41029
- 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) => {
41030
41148
  const client = await getClientAsync();
41031
41149
  const res = await client.pages.list({
41032
41150
  status: options.status,
41151
+ order: parseOrder(options.order),
41033
41152
  page: parseInt(options.page, 10),
41034
41153
  limit: parseInt(options.limit, 10)
41035
41154
  });
@@ -41126,9 +41245,9 @@ Examples:
41126
41245
  $ neopress collections update 12 --schema @schema.json
41127
41246
 
41128
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.`);
41129
- 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) => {
41130
41249
  const client = await getClientAsync();
41131
- const data = await client.collections.list();
41250
+ const data = await client.collections.list({ order: parseOrder(options.order) });
41132
41251
  output(data);
41133
41252
  });
41134
41253
  collections.command("get <collectionId>").description("Get collection details").action(async (collectionId) => {
@@ -41186,11 +41305,12 @@ Examples:
41186
41305
  $ neopress entries publish <entryId>
41187
41306
 
41188
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.`);
41189
- 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) => {
41190
41309
  const client = await getClientAsync();
41191
41310
  const res = await client.entries.list(parseInt(options.collection, 10), {
41192
41311
  status: options.status,
41193
41312
  locale: options.locale,
41313
+ order: parseOrder(options.order),
41194
41314
  page: parseInt(options.page, 10),
41195
41315
  limit: parseInt(options.limit, 10)
41196
41316
  });
@@ -41280,9 +41400,9 @@ Examples:
41280
41400
  $ neopress forms publish <formId>
41281
41401
  $ neopress forms responses <formId> --limit 50
41282
41402
  Forms are created as drafts \u2014 run \`neopress forms publish <formId>\` to make them live. JSON options take inline JSON or @file.`);
41283
- 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) => {
41284
41404
  const client = await getClientAsync();
41285
- const data = await client.forms.list();
41405
+ const data = await client.forms.list({ order: parseOrder(options.order) });
41286
41406
  output(data);
41287
41407
  });
41288
41408
  forms.command("get <formId>").description("Get form details").action(async (formId) => {
@@ -41362,9 +41482,10 @@ Examples:
41362
41482
  $ neopress assets upload ./hero.png --folder images
41363
41483
  $ neopress assets list --limit 50
41364
41484
  Upload runs presign \u2192 PUT to storage \u2192 register in one step; content type is inferred from the file extension.`);
41365
- 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) => {
41366
41486
  const client = await getClientAsync();
41367
41487
  const res = await client.assets.list({
41488
+ order: parseOrder(options.order),
41368
41489
  page: parseInt(options.page, 10),
41369
41490
  limit: parseInt(options.limit, 10)
41370
41491
  });
@@ -41447,9 +41568,9 @@ Examples:
41447
41568
  $ neopress redirects create --source /docs --dest /guide --type 308
41448
41569
  $ neopress redirects list
41449
41570
  Rules are created enabled. delete <ruleId> removes a rule permanently.`);
41450
- 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) => {
41451
41572
  const client = await getClientAsync();
41452
- const data = await client.redirects.list();
41573
+ const data = await client.redirects.list({ order: parseOrder(options.order) });
41453
41574
  output(data);
41454
41575
  });
41455
41576
  redirects.command("create").description("Create a redirect rule").addHelpText("after", `
@@ -41512,11 +41633,12 @@ Examples:
41512
41633
  $ neopress entry-references list --from-entry 12
41513
41634
  $ neopress entry-references delete <referenceId>
41514
41635
  --field is a reference field key from the source entry's collection schema. delete is a hard delete and removes the relation immediately.`);
41515
- 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) => {
41516
41637
  const client = await getClientAsync();
41517
41638
  const data = await client.entryReferences.list({
41518
41639
  fromEntryId: options.fromEntry ? parseInt(options.fromEntry, 10) : void 0,
41519
- toEntryId: options.toEntry ? parseInt(options.toEntry, 10) : void 0
41640
+ toEntryId: options.toEntry ? parseInt(options.toEntry, 10) : void 0,
41641
+ order: parseOrder(options.order)
41520
41642
  });
41521
41643
  output(data);
41522
41644
  });
@@ -41678,15 +41800,12 @@ Examples:
41678
41800
  $ neopress templates list --industry saas --limit 50
41679
41801
  $ neopress templates delete
41680
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.`);
41681
- 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) => {
41682
- const token = process.env.NEOPRESS_ACCESS_TOKEN || await getValidAccessToken() || void 0;
41683
- if (!token) {
41684
- fail("Not authenticated. Run `neopress login`.");
41685
- }
41686
- 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();
41687
41805
  const templates2 = await client.templates.list({
41688
41806
  namePrefix: options.namePrefix,
41689
41807
  industry: options.industry,
41808
+ order: parseOrder(options.order),
41690
41809
  limit: options.limit ? parseInt(options.limit, 10) : void 0
41691
41810
  });
41692
41811
  output(templates2);
@@ -41751,7 +41870,7 @@ Examples:
41751
41870
  $ neopress headshots create --image-url https://cdn/x.webp --age-band young --gender male --ethnicity white --variation-index 0
41752
41871
  $ neopress headshots update 12 --inactive
41753
41872
  The pool is global and RLS-gated: only the super-admin account can read or write it. No active site is needed.`);
41754
- 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", `
41755
41874
  Examples:
41756
41875
  $ neopress headshots list
41757
41876
  $ neopress headshots list --gender female --age-band middle --active
@@ -41766,6 +41885,7 @@ Filters are equality matches on the pool's demographic columns; results are pagi
41766
41885
  const params = {
41767
41886
  page: parseInt(options.page, 10),
41768
41887
  limit: parseInt(options.limit, 10),
41888
+ order: parseOrder(options.order),
41769
41889
  ageBand: options.ageBand,
41770
41890
  gender: options.gender,
41771
41891
  ethnicity: options.ethnicity,
@@ -41945,7 +42065,7 @@ function registerGuideCommands(program3) {
41945
42065
 
41946
42066
  // src/bin.ts
41947
42067
  var program2 = new Command();
41948
- program2.name("neopress").description("Neopress CLI \u2014 manage your site from the terminal").version("4.1.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) => {
41949
42069
  const opts = thisCommand.optsWithGlobals();
41950
42070
  if (opts.site) setSiteOverride(String(opts.site));
41951
42071
  if (opts.json || !process.stdout.isTTY) setJsonMode(true);
package/dist/index.js CHANGED
@@ -18,11 +18,11 @@ function __rest(s, e) {
18
18
  }
19
19
  function __awaiter(thisArg, _arguments, P, generator) {
20
20
  function adopt(value) {
21
- return value instanceof P ? value : new P(function(resolve) {
22
- resolve(value);
21
+ return value instanceof P ? value : new P(function(resolve2) {
22
+ resolve2(value);
23
23
  });
24
24
  }
25
- return new (P || (P = Promise))(function(resolve, reject) {
25
+ return new (P || (P = Promise))(function(resolve2, reject) {
26
26
  function fulfilled(value) {
27
27
  try {
28
28
  step(generator.next(value));
@@ -38,7 +38,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
38
38
  }
39
39
  }
40
40
  function step(result) {
41
- result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
41
+ result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
42
42
  }
43
43
  step((generator = generator.apply(thisArg, _arguments || [])).next());
44
44
  });
@@ -409,18 +409,18 @@ var PostgrestError = class extends Error {
409
409
  }
410
410
  };
411
411
  function sleep(ms, signal) {
412
- return new Promise((resolve) => {
412
+ return new Promise((resolve2) => {
413
413
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
414
- resolve();
414
+ resolve2();
415
415
  return;
416
416
  }
417
417
  const id = setTimeout(() => {
418
418
  signal === null || signal === void 0 || signal.removeEventListener("abort", onAbort);
419
- resolve();
419
+ resolve2();
420
420
  }, ms);
421
421
  function onAbort() {
422
422
  clearTimeout(id);
423
- resolve();
423
+ resolve2();
424
424
  }
425
425
  signal === null || signal === void 0 || signal.addEventListener("abort", onAbort);
426
426
  });
@@ -8369,15 +8369,15 @@ var RealtimeChannel = class _RealtimeChannel {
8369
8369
  }
8370
8370
  }
8371
8371
  } else {
8372
- return new Promise((resolve) => {
8372
+ return new Promise((resolve2) => {
8373
8373
  var _a3, _b2, _c;
8374
8374
  const push = this.channelAdapter.push(args.type, args, opts.timeout || this.timeout);
8375
8375
  if (args.type === "broadcast" && !((_c = (_b2 = (_a3 = this.params) === null || _a3 === void 0 ? void 0 : _a3.config) === null || _b2 === void 0 ? void 0 : _b2.broadcast) === null || _c === void 0 ? void 0 : _c.ack)) {
8376
- resolve("ok");
8376
+ resolve2("ok");
8377
8377
  }
8378
- push.receive("ok", () => resolve("ok"));
8379
- push.receive("error", () => resolve("error"));
8380
- push.receive("timeout", () => resolve("timed out"));
8378
+ push.receive("ok", () => resolve2("ok"));
8379
+ push.receive("error", () => resolve2("error"));
8380
+ push.receive("timeout", () => resolve2("timed out"));
8381
8381
  });
8382
8382
  }
8383
8383
  }
@@ -8402,8 +8402,8 @@ var RealtimeChannel = class _RealtimeChannel {
8402
8402
  * @category Realtime
8403
8403
  */
8404
8404
  async unsubscribe(timeout = this.timeout) {
8405
- return new Promise((resolve) => {
8406
- this.channelAdapter.unsubscribe(timeout).receive("ok", () => resolve("ok")).receive("timeout", () => resolve("timed out")).receive("error", () => resolve("error"));
8405
+ return new Promise((resolve2) => {
8406
+ this.channelAdapter.unsubscribe(timeout).receive("ok", () => resolve2("ok")).receive("timeout", () => resolve2("timed out")).receive("error", () => resolve2("error"));
8407
8407
  });
8408
8408
  }
8409
8409
  /**
@@ -8484,8 +8484,8 @@ var RealtimeChannel = class _RealtimeChannel {
8484
8484
  }
8485
8485
  /** @internal */
8486
8486
  _notThisChannelEvent(event, ref) {
8487
- const { close, error: error48, leave, join: join2 } = CHANNEL_EVENTS;
8488
- const events = [close, error48, leave, join2];
8487
+ const { close, error: error48, leave, join: join3 } = CHANNEL_EVENTS;
8488
+ const events = [close, error48, leave, join3];
8489
8489
  return ref && events.includes(event) && ref !== this.joinPush.ref;
8490
8490
  }
8491
8491
  /** @internal */
@@ -8598,11 +8598,11 @@ var SocketAdapter = class {
8598
8598
  this.socket.connect();
8599
8599
  }
8600
8600
  disconnect(callback, code, reason, timeout = 1e4) {
8601
- return new Promise((resolve) => {
8602
- setTimeout(() => resolve("timeout"), timeout);
8601
+ return new Promise((resolve2) => {
8602
+ setTimeout(() => resolve2("timeout"), timeout);
8603
8603
  this.socket.disconnect(() => {
8604
8604
  callback();
8605
- resolve("ok");
8605
+ resolve2("ok");
8606
8606
  }, code, reason);
8607
8607
  });
8608
8608
  }
@@ -10016,7 +10016,7 @@ var _getRequestParams = (method, options, parameters, body) => {
10016
10016
  return _objectSpread22(_objectSpread22({}, params), parameters);
10017
10017
  };
10018
10018
  async function _handleRequest(fetcher, method, url2, options, parameters, body, namespace) {
10019
- return new Promise((resolve, reject) => {
10019
+ return new Promise((resolve2, reject) => {
10020
10020
  fetcher(url2, _getRequestParams(method, options, parameters, body)).then((result) => {
10021
10021
  if (!result.ok) throw result;
10022
10022
  if (options === null || options === void 0 ? void 0 : options.noResolveJson) return result;
@@ -10026,7 +10026,7 @@ async function _handleRequest(fetcher, method, url2, options, parameters, body,
10026
10026
  if (!contentType || !contentType.includes("application/json")) return {};
10027
10027
  }
10028
10028
  return result.json();
10029
- }).then((data) => resolve(data)).catch((error48) => handleError(error48, reject, options, namespace));
10029
+ }).then((data) => resolve2(data)).catch((error48) => handleError(error48, reject, options, namespace));
10030
10030
  });
10031
10031
  }
10032
10032
  function createFetchApi(namespace = "storage") {
@@ -20271,11 +20271,11 @@ var DEFAULT_TRACE_PROPAGATION_OPTIONS = {
20271
20271
  };
20272
20272
  function __awaiter2(thisArg, _arguments, P, generator) {
20273
20273
  function adopt(value) {
20274
- return value instanceof P ? value : new P(function(resolve) {
20275
- resolve(value);
20274
+ return value instanceof P ? value : new P(function(resolve2) {
20275
+ resolve2(value);
20276
20276
  });
20277
20277
  }
20278
- return new (P || (P = Promise))(function(resolve, reject) {
20278
+ return new (P || (P = Promise))(function(resolve2, reject) {
20279
20279
  function fulfilled(value) {
20280
20280
  try {
20281
20281
  step(generator.next(value));
@@ -20291,7 +20291,7 @@ function __awaiter2(thisArg, _arguments, P, generator) {
20291
20291
  }
20292
20292
  }
20293
20293
  function step(result) {
20294
- result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
20294
+ result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
20295
20295
  }
20296
20296
  step((generator = generator.apply(thisArg, _arguments || [])).next());
20297
20297
  });
@@ -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);
@@ -35513,8 +35514,8 @@ var SiteHistoryService = class _SiteHistoryService {
35513
35514
  const previousLock = _SiteHistoryService.sessionLocks.get(key) ?? Promise.resolve();
35514
35515
  let releaseCurrentLock = () => {
35515
35516
  };
35516
- const currentLock = new Promise((resolve) => {
35517
- releaseCurrentLock = resolve;
35517
+ const currentLock = new Promise((resolve2) => {
35518
+ releaseCurrentLock = resolve2;
35518
35519
  });
35519
35520
  const queuedLock = previousLock.catch(() => {
35520
35521
  }).then(() => currentLock);
@@ -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 {
@@ -37201,23 +37257,44 @@ function fail(message, code = 1) {
37201
37257
 
37202
37258
  // src/utils/config.ts
37203
37259
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
37204
- import { dirname } from "path";
37260
+ import { dirname, join as join2, resolve } from "path";
37261
+ import { homedir as homedir2 } from "os";
37205
37262
  var PROJECT_CONFIG = ".neopressrc.json";
37206
37263
  var WORKSPACE_CONFIG = ".neopress/config.json";
37207
- function readProjectConfig() {
37208
- let config2 = {};
37209
- for (const file2 of [PROJECT_CONFIG, WORKSPACE_CONFIG]) {
37210
- try {
37211
- const parsed = JSON.parse(readFileSync2(file2, "utf-8"));
37212
- config2 = {
37213
- ...config2,
37214
- ...typeof parsed.siteId === "number" ? { siteId: parsed.siteId } : {},
37215
- ...typeof parsed.baseUrl === "string" ? { baseUrl: parsed.baseUrl } : {}
37216
- };
37217
- } catch {
37264
+ function readConfigFileAt(path) {
37265
+ try {
37266
+ const parsed = JSON.parse(readFileSync2(path, "utf-8"));
37267
+ return {
37268
+ ...typeof parsed.siteId === "number" ? { siteId: parsed.siteId } : {},
37269
+ ...typeof parsed.baseUrl === "string" ? { baseUrl: parsed.baseUrl } : {}
37270
+ };
37271
+ } catch {
37272
+ return {};
37273
+ }
37274
+ }
37275
+ function locateProjectConfig(startDir = process.cwd()) {
37276
+ const home = resolve(homedir2());
37277
+ let dir = resolve(startDir);
37278
+ while (true) {
37279
+ let merged = {};
37280
+ let foundPath;
37281
+ for (const rel of [PROJECT_CONFIG, WORKSPACE_CONFIG]) {
37282
+ const p = join2(dir, rel);
37283
+ const cfg = readConfigFileAt(p);
37284
+ if (Object.keys(cfg).length > 0) {
37285
+ merged = { ...merged, ...cfg };
37286
+ if (!foundPath) foundPath = p;
37287
+ }
37218
37288
  }
37289
+ if (Object.keys(merged).length > 0) return { config: merged, path: foundPath };
37290
+ const parent = dirname(dir);
37291
+ if (dir === home || parent === dir) break;
37292
+ dir = parent;
37219
37293
  }
37220
- return config2;
37294
+ return { config: {} };
37295
+ }
37296
+ function readProjectConfig() {
37297
+ return locateProjectConfig().config;
37221
37298
  }
37222
37299
  function writeWorkspaceConfig(next) {
37223
37300
  mkdirSync2(dirname(WORKSPACE_CONFIG), { recursive: true });
@@ -37244,12 +37321,24 @@ function setActiveSiteId(siteId) {
37244
37321
  }
37245
37322
  }
37246
37323
  var siteOverride;
37247
- function getSiteTarget() {
37324
+ function resolveSiteTarget() {
37248
37325
  if (siteOverride) {
37249
37326
  const n = parseInt(siteOverride, 10);
37250
- return String(n) === siteOverride ? n : siteOverride;
37327
+ const value = String(n) === siteOverride ? n : siteOverride;
37328
+ return { value, source: "flag", detail: "--site" };
37329
+ }
37330
+ const envSiteId = process.env.NEOPRESS_SITE_ID;
37331
+ if (envSiteId) return { value: parseInt(envSiteId, 10), source: "env", detail: "NEOPRESS_SITE_ID" };
37332
+ const located = locateProjectConfig();
37333
+ if (typeof located.config.siteId === "number") {
37334
+ return { value: located.config.siteId, source: "project-config", detail: located.path };
37251
37335
  }
37252
- return getSiteId();
37336
+ const active = loadSession().activeSiteId;
37337
+ if (active) return { value: active, source: "global-active", detail: "sites use" };
37338
+ return { value: void 0, source: "none" };
37339
+ }
37340
+ function getSiteTarget() {
37341
+ return resolveSiteTarget().value;
37253
37342
  }
37254
37343
 
37255
37344
  // src/utils/client.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neopress/cli",
3
- "version": "4.1.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": [