@neopress/cli 4.9.0 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -19,7 +19,7 @@ interface PaginatedResponse<T> {
19
19
  pagination: PaginationMeta;
20
20
  }
21
21
  /** Sort direction for list endpoints that support opt-in created_at ordering. */
22
- type SortOrder = 'asc' | 'desc';
22
+ type SortOrder = "asc" | "desc";
23
23
  interface Site {
24
24
  id: number;
25
25
  name: string | null;
@@ -32,7 +32,7 @@ interface Site {
32
32
  localeConfig: {
33
33
  defaultLocale: string;
34
34
  locales: string[];
35
- localePrefix?: 'as-needed';
35
+ localePrefix?: "as-needed";
36
36
  } | null;
37
37
  createdAt: string | null;
38
38
  updatedAt: string | null;
@@ -169,7 +169,7 @@ interface RedirectRule {
169
169
  siteId: number;
170
170
  source: string;
171
171
  destination: string;
172
- mode: 'redirect' | 'proxy';
172
+ mode: "redirect" | "proxy";
173
173
  redirectType: number;
174
174
  includeChildren: boolean;
175
175
  preservePath: boolean;
@@ -255,7 +255,7 @@ interface AnalyticsDateRangeParams {
255
255
  limit?: number;
256
256
  params?: Record<string, AnalyticsQueryValue>;
257
257
  }
258
- type SearchConsoleDimension = 'query' | 'page' | 'country' | 'device';
258
+ type SearchConsoleDimension = "query" | "page" | "country" | "device";
259
259
  interface SearchConsoleQueryParams {
260
260
  dateFrom: string;
261
261
  dateTo: string;
@@ -282,12 +282,108 @@ interface SearchConsoleQueryResult {
282
282
  };
283
283
  error?: string;
284
284
  }
285
+ type AnalyticsEventType = "custom" | "page" | "click" | "form" | "scroll" | "visibility" | "dwell";
286
+ type FunnelFilterField = "event.path" | "event.url" | "event.referrer" | "event.referrer_source" | "event.property" | "session.referrer" | "session.referrer_source" | "session.traffic_category" | "session.utm_source" | "session.utm_medium" | "session.utm_campaign" | "session.utm_content" | "session.utm_term";
287
+ type FunnelFilterOperator = "eq" | "neq" | "contains" | "not_contains" | "starts_with" | "ends_with" | "exists" | "not_exists" | "gt" | "gte" | "lt" | "lte";
288
+ type AnalyticsFunnelFilterCondition = {
289
+ kind: "condition";
290
+ field: FunnelFilterField;
291
+ operator: FunnelFilterOperator;
292
+ key?: string;
293
+ value?: string | number;
294
+ };
295
+ type AnalyticsFunnelFilterGroup = {
296
+ kind: "group";
297
+ op: "and" | "or";
298
+ children: AnalyticsFunnelFilterNode[];
299
+ };
300
+ type AnalyticsFunnelFilterNode = AnalyticsFunnelFilterGroup | AnalyticsFunnelFilterCondition;
301
+ type AnalyticsFunnelFilter = {
302
+ version: 1;
303
+ root: AnalyticsFunnelFilterGroup;
304
+ };
305
+ interface AnalyticsEventDefinition {
306
+ id: string;
307
+ chart_color: string | null;
308
+ name: string;
309
+ chart_visible: boolean;
310
+ is_pinned: boolean;
311
+ event_type: AnalyticsEventType;
312
+ event_name: string;
313
+ path: string;
314
+ selector: string;
315
+ form_id: string;
316
+ scroll_depth: number;
317
+ dwell_seconds: number;
318
+ tracking_version: number;
319
+ created_at: string;
320
+ updated_at: string;
321
+ }
322
+ interface AnalyticsEventDefinitionInput {
323
+ /** Optional client UUID; reuse it with the same payload after an uncertain create response. */
324
+ id?: string;
325
+ name: string;
326
+ eventType: AnalyticsEventType;
327
+ eventName?: string;
328
+ path?: string;
329
+ selector?: string;
330
+ formId?: string;
331
+ scrollDepth?: number;
332
+ dwellSeconds?: number;
333
+ }
334
+ type AnalyticsEventDefinitionUpdate = AnalyticsEventDefinitionInput | {
335
+ name: string;
336
+ };
337
+ interface AnalyticsEventDisplayUpdate {
338
+ chartVisible?: boolean;
339
+ pinned?: boolean;
340
+ chartColor?: string;
341
+ }
342
+ interface AnalyticsFunnelStepInput {
343
+ eventId: string;
344
+ path?: string;
345
+ filter?: AnalyticsFunnelFilter;
346
+ }
347
+ interface AnalyticsFunnelStep {
348
+ event_id: string;
349
+ path?: string;
350
+ filter?: AnalyticsFunnelFilter;
351
+ }
352
+ interface AnalyticsFunnelDefinition {
353
+ invalid?: true;
354
+ id: string;
355
+ name: string;
356
+ steps: AnalyticsFunnelStep[];
357
+ session_only: boolean;
358
+ window_minutes: number;
359
+ created_at: string;
360
+ updated_at: string;
361
+ }
362
+ interface AnalyticsTargets {
363
+ pages: Array<{
364
+ id: number;
365
+ name: string;
366
+ path: string;
367
+ locale: string;
368
+ }>;
369
+ forms: Array<{
370
+ id: number;
371
+ name: string;
372
+ }>;
373
+ }
374
+ interface AnalyticsFunnelDefinitionInput {
375
+ id?: string;
376
+ name: string;
377
+ steps: AnalyticsFunnelStepInput[];
378
+ windowMode: "session" | "time";
379
+ windowHours?: number;
380
+ }
285
381
  //#endregion
286
382
  //#region ../sdk/src/endpoints/pages.d.ts
287
383
  interface PageListParams {
288
384
  page?: number;
289
385
  limit?: number;
290
- status?: 'draft' | 'published';
386
+ status?: "draft" | "published";
291
387
  fields?: string[];
292
388
  /** When set, sort by created_at instead of path. */
293
389
  order?: SortOrder;
@@ -295,9 +391,9 @@ interface PageListParams {
295
391
  interface PageCreateParams {
296
392
  path: string;
297
393
  title?: string;
298
- type?: 'static' | 'dynamic';
299
- status?: 'draft' | 'published';
300
- visibility?: 'public' | 'hidden';
394
+ type?: "static" | "dynamic";
395
+ status?: "draft" | "published";
396
+ visibility?: "public" | "hidden";
301
397
  draftTsx?: string;
302
398
  draftI18n?: unknown;
303
399
  draftPublicLocales?: string[] | null;
@@ -310,7 +406,7 @@ interface PageUpdateParams {
310
406
  title?: string;
311
407
  type?: string;
312
408
  status?: string;
313
- visibility?: 'public' | 'hidden';
409
+ visibility?: string;
314
410
  draftTsx?: string;
315
411
  draftI18n?: unknown;
316
412
  draftPublicLocales?: string[] | null;
@@ -347,6 +443,12 @@ declare class CollectionsEndpoint {
347
443
  order?: SortOrder;
348
444
  }): Promise<Collection[]>;
349
445
  get(collectionId: number): Promise<Collection>;
446
+ /**
447
+ * Preset normalization (`normalizeCollectionPresetType` +
448
+ * `ensureCollectionPresetBaselineFields`) now runs server-side in
449
+ * `CollectionsApi.create`, which applies the identical pair in the identical
450
+ * order — the stored row is unchanged by the move to HTTP.
451
+ */
350
452
  create(data: CollectionCreateParams): Promise<Collection>;
351
453
  update(collectionId: number, data: Partial<CollectionCreateParams>): Promise<Collection>;
352
454
  delete(collectionId: number): Promise<void>;
@@ -355,10 +457,6 @@ declare class CollectionsEndpoint {
355
457
  draftSchema: unknown;
356
458
  prodSchema: unknown;
357
459
  }>;
358
- /**
359
- * Updates the collection's draft_schema directly in Supabase.
360
- * validateApiCollectionSchema (app-local) is skipped — CLI/MCP callers are trusted.
361
- */
362
460
  updateSchema(collectionId: number, schema: unknown): Promise<{
363
461
  collectionId: number;
364
462
  draftSchema: unknown;
@@ -370,7 +468,7 @@ declare class CollectionsEndpoint {
370
468
  interface EntryListParams {
371
469
  page?: number;
372
470
  limit?: number;
373
- status?: 'draft' | 'published' | 'archived';
471
+ status?: "draft" | "published" | "archived";
374
472
  locale?: string;
375
473
  /** created_at sort direction. Defaults to 'desc' (newest first). */
376
474
  order?: SortOrder;
@@ -394,9 +492,12 @@ interface EntryUpdateParams {
394
492
  declare class EntriesEndpoint {
395
493
  private readonly client;
396
494
  constructor(client: NeopressClient);
495
+ private get basePath();
397
496
  list(collectionId: number, params?: EntryListParams): Promise<PaginatedResponse<Entry>>;
398
497
  get(entryId: number): Promise<Entry>;
399
498
  create(collectionId: number, data: EntryCreateParams): Promise<Entry>;
499
+ /** Copy source content into a linked locale draft, or return its existing translation. */
500
+ createTranslation(entryId: number, locale: string): Promise<Entry>;
400
501
  update(entryId: number, data: EntryUpdateParams): Promise<Entry>;
401
502
  delete(entryId: number): Promise<void>;
402
503
  revalidateReaderCache(entryId: number): Promise<void>;
@@ -484,7 +585,12 @@ declare class AssetsEndpoint {
484
585
  }): Promise<PaginatedResponse<Asset>>;
485
586
  get(assetId: number): Promise<Asset>;
486
587
  register(data: AssetRegisterParams): Promise<Asset>;
487
- update(assetId: number, data: Partial<Omit<AssetRegisterParams, 'assetUrl'>>): Promise<Asset>;
588
+ update(assetId: number, data: Partial<Omit<AssetRegisterParams, "assetUrl">>): Promise<Asset>;
589
+ /**
590
+ * Hard delete. The route additionally requires the caller to be the site
591
+ * owner, matching `assets_delete_policy` = `is_site_owner(site_id)` — the
592
+ * registry axes alone would have widened this (W7 mapping §3-B1).
593
+ */
488
594
  delete(assetId: number): Promise<void>;
489
595
  presign(data: AssetPresignParams): Promise<{
490
596
  presignedUrl: string;
@@ -495,6 +601,12 @@ declare class AssetsEndpoint {
495
601
  //#endregion
496
602
  //#region ../sdk/src/endpoints/site.d.ts
497
603
  interface SiteWithRole extends Site {
604
+ /** Project role of the caller on this site: "owner" | "admin" | "member". */
605
+ projectRole: string;
606
+ /**
607
+ * @deprecated Legacy site role derived from `projectRole` for backwards
608
+ * compatibility ("owner" | "admin" | "member"). Use `projectRole` instead.
609
+ */
498
610
  role: string;
499
611
  }
500
612
  interface SiteCreateParams {
@@ -502,18 +614,25 @@ interface SiteCreateParams {
502
614
  siteHandle?: string;
503
615
  prompt?: string;
504
616
  language?: string;
505
- placeholder?: boolean;
506
617
  }
507
618
  declare class SiteEndpoint {
508
619
  private readonly client;
509
620
  constructor(client: NeopressClient);
621
+ /**
622
+ * Global (not site-scoped): lists the caller's own memberships, so it does not
623
+ * hang off `siteBasePath`. The membership + soft-delete filters that RLS used
624
+ * to apply now live in the route.
625
+ */
510
626
  list(params?: {
511
627
  order?: SortOrder;
512
628
  }): Promise<SiteWithRole[]>;
513
629
  get(): Promise<Site>;
514
630
  create(data: SiteCreateParams): Promise<Site>;
515
- update(data: Partial<Pick<Site, 'name' | 'metadata' | 'localeConfig'>> & {
516
- globalCss?: string;
631
+ /**
632
+ * Site settings only. The stylesheet is not here — it lives on the layout
633
+ * (`updateLayout({ draftGlobalCss })`), which is what the reader renders.
634
+ */
635
+ update(data: Partial<Pick<Site, "name" | "metadata" | "localeConfig">> & {
517
636
  instructions?: unknown;
518
637
  }): Promise<Site>;
519
638
  getLayout(): Promise<SiteLayout>;
@@ -541,11 +660,17 @@ interface RedirectCreateParams {
541
660
  declare class RedirectsEndpoint {
542
661
  private readonly client;
543
662
  constructor(client: NeopressClient);
663
+ private get basePath();
544
664
  list(params?: {
545
665
  order?: SortOrder;
546
666
  }): Promise<RedirectRule[]>;
547
667
  create(data: RedirectCreateParams): Promise<RedirectRule>;
548
- private assertRedirectRuleWritable;
668
+ /**
669
+ * The proxy preflight that used to run here (`assertRedirectRuleWritable`)
670
+ * needs a DB read, so it moved into the route. Its two error codes —
671
+ * `REDIRECT_RULE_NOT_FOUND` and `REDIRECT_RULE_DASHBOARD_ONLY` — are returned
672
+ * by the server verbatim, so `NeopressApiError.code` is unchanged for callers.
673
+ */
549
674
  update(ruleId: number, data: Partial<RedirectCreateParams>): Promise<RedirectRule>;
550
675
  delete(ruleId: number): Promise<void>;
551
676
  }
@@ -573,6 +698,7 @@ interface EntryReferenceCreateParams {
573
698
  declare class EntryReferencesEndpoint {
574
699
  private readonly client;
575
700
  constructor(client: NeopressClient);
701
+ private get basePath();
576
702
  list(params?: EntryReferenceListParams): Promise<EntryReference[]>;
577
703
  create(data: EntryReferenceCreateParams): Promise<EntryReference>;
578
704
  delete(referenceId: number): Promise<void>;
@@ -581,6 +707,8 @@ declare class EntryReferencesEndpoint {
581
707
  //#region ../sdk/src/endpoints/analytics.d.ts
582
708
  declare class AnalyticsEndpoint {
583
709
  private readonly client;
710
+ readonly events: AnalyticsEventsEndpoint;
711
+ readonly funnels: AnalyticsFunnelsEndpoint;
584
712
  constructor(client: NeopressClient);
585
713
  private get basePath();
586
714
  query(params: AnalyticsQueryParams): Promise<AnalyticsQueryResult>;
@@ -591,6 +719,30 @@ declare class AnalyticsEndpoint {
591
719
  formAnalytics(params?: AnalyticsDateRangeParams): Promise<AnalyticsQueryResult>;
592
720
  crawlerSummary(params?: AnalyticsDateRangeParams): Promise<AnalyticsQueryResult>;
593
721
  searchConsole(params: SearchConsoleQueryParams): Promise<SearchConsoleQueryResult>;
722
+ /** Page/form targets that can be referenced by an event definition. */
723
+ targets(): Promise<AnalyticsTargets>;
724
+ }
725
+ declare class AnalyticsEventsEndpoint {
726
+ private readonly client;
727
+ constructor(client: NeopressClient);
728
+ private get basePath();
729
+ list(): Promise<AnalyticsEventDefinition[]>;
730
+ get(eventId: string): Promise<AnalyticsEventDefinition>;
731
+ create(data: AnalyticsEventDefinitionInput): Promise<AnalyticsEventDefinition>;
732
+ update(eventId: string, data: AnalyticsEventDefinitionUpdate | {
733
+ display: AnalyticsEventDisplayUpdate;
734
+ }): Promise<AnalyticsEventDefinition>;
735
+ delete(eventId: string): Promise<void>;
736
+ }
737
+ declare class AnalyticsFunnelsEndpoint {
738
+ private readonly client;
739
+ constructor(client: NeopressClient);
740
+ private get basePath();
741
+ list(): Promise<AnalyticsFunnelDefinition[]>;
742
+ get(funnelId: string): Promise<AnalyticsFunnelDefinition>;
743
+ create(data: AnalyticsFunnelDefinitionInput): Promise<AnalyticsFunnelDefinition>;
744
+ update(funnelId: string, data: AnalyticsFunnelDefinitionInput): Promise<AnalyticsFunnelDefinition>;
745
+ delete(funnelId: string): Promise<void>;
594
746
  }
595
747
  //#endregion
596
748
  //#region ../sdk/src/endpoints/templates.d.ts
@@ -623,9 +775,8 @@ declare class TemplatesEndpoint {
623
775
  /**
624
776
  * List templates across the sites the caller can access.
625
777
  *
626
- * Direct-DB: `templates` is public-read and `sites` RLS scopes the visible set
627
- * (own active memberships, or all sites for the super admin), so no server
628
- * route is needed — the two RLS-scoped queries reproduce the old handler.
778
+ * Global (not site-scoped) — the accessible-site set is resolved server-side,
779
+ * where RLS used to do it.
629
780
  */
630
781
  list(params?: {
631
782
  namePrefix?: string;
@@ -635,10 +786,10 @@ declare class TemplatesEndpoint {
635
786
  }
636
787
  //#endregion
637
788
  //#region ../sdk/src/endpoints/headshots.d.ts
638
- type HeadshotAgeBand = 'young' | 'middle';
639
- type HeadshotGender = 'male' | 'female';
640
- type HeadshotEthnicity = 'asian' | 'white' | 'black' | 'latino';
641
- type HeadshotAttire = 'formal' | 'casual';
789
+ type HeadshotAgeBand = "young" | "middle";
790
+ type HeadshotGender = "male" | "female";
791
+ type HeadshotEthnicity = "asian" | "white" | "black" | "latino";
792
+ type HeadshotAttire = "formal" | "casual";
642
793
  interface HeadshotListParams {
643
794
  page?: number;
644
795
  limit?: number;
@@ -687,9 +838,11 @@ interface HeadshotDeleteResult {
687
838
  deleted: true;
688
839
  }
689
840
  /**
690
- * headshot_pool is a GLOBAL (not site-scoped) admin table. RLS exposes it only to
691
- * the super-admin user, so these direct-DB calls return empty/forbidden for any
692
- * other account. No site id is used.
841
+ * `headshot_pool` is a GLOBAL (not site-scoped) admin table, so these calls run
842
+ * over `/api/v1/headshots` — no `siteId` in the path — instead of the
843
+ * site-scoped `withAuth` routes (W7 T5). The routes gate on the super-admin
844
+ * user id, which is the same check the table's single RLS policy made; a
845
+ * non-super-admin now gets 403 where the direct-DB read returned an empty list.
693
846
  */
694
847
  declare class HeadshotsEndpoint {
695
848
  private readonly client;
@@ -702,8 +855,8 @@ declare class HeadshotsEndpoint {
702
855
  }
703
856
  //#endregion
704
857
  //#region ../sdk/src/endpoints/logos.d.ts
705
- type LogoType = 'wordmark' | 'lockup' | 'signature';
706
- type LogoTone = 'playful' | 'neutral' | 'serious';
858
+ type LogoType = "wordmark" | "lockup" | "signature";
859
+ type LogoTone = "playful" | "neutral" | "serious";
707
860
  interface LogoListParams {
708
861
  page?: number;
709
862
  limit?: number;
@@ -758,9 +911,11 @@ interface LogoDeleteResult {
758
911
  deleted: true;
759
912
  }
760
913
  /**
761
- * logo_pool is a GLOBAL (not site-scoped) admin table. RLS exposes it only to the
762
- * super-admin user, so these direct-DB calls return empty/forbidden for any other
763
- * account. No site id is used.
914
+ * `logo_pool` is a GLOBAL (not site-scoped) admin table, so these calls run over
915
+ * `/api/v1/logos` — no `siteId` in the path — instead of the site-scoped
916
+ * `withAuth` routes (W7 T5). The routes gate on the super-admin user id, which
917
+ * is the same check the table's single RLS policy made; a non-super-admin now
918
+ * gets 403 where the direct-DB read returned an empty list.
764
919
  */
765
920
  declare class LogosEndpoint {
766
921
  private readonly client;
@@ -773,28 +928,29 @@ declare class LogosEndpoint {
773
928
  }
774
929
  //#endregion
775
930
  //#region ../sdk/src/client.d.ts
931
+ /**
932
+ * ⚠️ No `supabaseUrl` / `supabaseAnonKey`. This client speaks HTTP to
933
+ * `/api/v1/**` and nothing else — the direct-DB escape hatch (`client.db`) was
934
+ * removed in W7 so the seven tables it wrote to could have their
935
+ * `authenticated` write grants revoked
936
+ * (`docs/plan/2026-08-17-w7-sdk-retirement.md`). `check-dist-contract.mjs` in
937
+ * `@neopress/cli` / `@neopress/mcp` fails the build if either option returns.
938
+ */
776
939
  interface NeopressClientOptions {
777
940
  /** OAuth access token */
778
941
  accessToken?: string;
779
942
  /** Site ID to operate on */
780
943
  siteId: number | string;
781
- /** Base URL (default: https://app.neopress.ai) — used by orchestration/analytics (HTTP) routes */
944
+ /** Base URL (default: https://app.neopress.ai) */
782
945
  baseUrl?: string;
783
946
  /** Request timeout in ms (default: 30000) */
784
947
  timeout?: number;
785
- /** Supabase project URL — enables direct-DB reads/CRUD (transparent routing). */
786
- supabaseUrl?: string;
787
- /** Supabase anon key (paired with the user accessToken so RLS applies). */
788
- supabaseAnonKey?: string;
789
948
  }
790
949
  declare class NeopressClient {
791
950
  private readonly baseUrl;
792
951
  private readonly accessToken;
793
952
  private readonly siteId;
794
953
  private readonly timeout;
795
- private readonly supabaseUrl?;
796
- private readonly supabaseAnonKey?;
797
- private _db;
798
954
  readonly pages: PagesEndpoint;
799
955
  readonly collections: CollectionsEndpoint;
800
956
  readonly entries: EntriesEndpoint;
@@ -810,26 +966,6 @@ declare class NeopressClient {
810
966
  readonly logos: LogosEndpoint;
811
967
  constructor(options: NeopressClientOptions);
812
968
  get siteBasePath(): string;
813
- /** Numeric site id for direct DB queries. */
814
- get siteIdNumber(): number;
815
- /**
816
- * Authenticated user's profile id, decoded locally from the access token's
817
- * JWT `sub` claim (no network round-trip). Returns null if there is no token
818
- * or it cannot be parsed. Subsequent DB queries are RLS-protected, so a local
819
- * decode is sufficient — no server-side validation is required here.
820
- */
821
- get authUserId(): string | null;
822
- /** Mirror of v1 parsePagination(defaultLimit=20, maxLimit=100). */
823
- resolvePagination(params?: {
824
- page?: number;
825
- limit?: number;
826
- }): {
827
- page: number;
828
- limit: number;
829
- offset: number;
830
- };
831
- /** Build the paginated envelope matching the v1 API. */
832
- paginationMeta(page: number, limit: number, offset: number, totalCount: number): PaginationMeta;
833
969
  request<T>(method: string, path: string, body?: unknown): Promise<T>;
834
970
  get<T>(path: string): Promise<ApiResponse<T>>;
835
971
  getList<T>(path: string): Promise<PaginatedResponse<T>>;