@shipstatic/types 2.5.0-beta.2 → 2.5.0-beta.4

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
@@ -201,6 +201,8 @@ export interface TokenListItem {
201
201
  export interface TokenListResponse {
202
202
  /** Array of tokens (security-redacted for list display) */
203
203
  tokens: TokenListItem[];
204
+ /** Cursor for pagination, null if no more pages */
205
+ cursor: string | null;
204
206
  /** Total number of tokens */
205
207
  total: number;
206
208
  }
@@ -645,6 +647,36 @@ export declare const SPA_DEFAULT_CONFIG: {
645
647
  readonly destination: "/index.html";
646
648
  }];
647
649
  };
650
+ /**
651
+ * Assert that a ship.json file is *syntactically* loadable. Syntax only —
652
+ * never schema.
653
+ *
654
+ * ship.json is validated and compiled on the server, deliberately: the schema
655
+ * and the compiler evolve, and a client that judged them would reject configs
656
+ * a newer platform accepts. That reasoning bounds what a client may check to
657
+ * the properties which are true of *every* past and future schema:
658
+ *
659
+ * 1. it parses as JSON — JSON syntax is frozen (RFC 8259), so text that
660
+ * does not parse can never be a valid config;
661
+ * 2. its top level is an object — ship.json is `{ ... }` in every version.
662
+ *
663
+ * Both are monotonic: neither can ever reject something the server would
664
+ * accept. Everything beyond them (field names, types, rule semantics, which
665
+ * keys are permitted) stays server-side, where it can change.
666
+ *
667
+ * The payoff is the common case. Hand-edited JSON fails on a trailing comma,
668
+ * a `//` comment, single quotes, unquoted keys, or smart quotes pasted from
669
+ * documentation — mistakes that otherwise cost a full upload round-trip to
670
+ * discover. A UTF-8 BOM (Windows editors, PowerShell redirects) is stripped
671
+ * before parsing rather than rejected, because the server accepts it too;
672
+ * diverging there would reintroduce exactly the false rejection this
673
+ * function exists to avoid.
674
+ *
675
+ * @throws {ShipError} `ErrorType.Config` — the same type the server's own
676
+ * config rejection carries, so the error contract is identical wherever the
677
+ * failure is detected.
678
+ */
679
+ export declare function assertShipJsonSyntax(text: string): void;
648
680
  /**
649
681
  * Validate API key format
650
682
  */
@@ -932,6 +964,10 @@ export interface ActivityMeta {
932
964
  export interface ActivityListResponse {
933
965
  /** Array of activities */
934
966
  activities: Activity[];
967
+ /** Cursor for pagination, null if no more pages */
968
+ cursor: string | null;
969
+ /** Total number of activities */
970
+ total: number;
935
971
  }
936
972
  /**
937
973
  * File status constants for validation state tracking
package/dist/index.js CHANGED
@@ -100,11 +100,19 @@ const CLIENT_ONLY_ERROR_TYPES = new Set([
100
100
  * union so `.has(error.type)` accepts any value from the union.
101
101
  */
102
102
  const ERROR_CATEGORIES = {
103
+ /**
104
+ * Every 4xx-class type — the caller's request or state is at fault, and
105
+ * the authored message is safe to surface verbatim. The set is exhaustive
106
+ * on purpose: a partial one forces consumers to add a status-range check
107
+ * beside every `isClientError()` call for the types it forgot.
108
+ */
103
109
  client: new Set([
104
110
  ErrorType.Business,
105
111
  ErrorType.Config,
106
112
  ErrorType.File,
107
113
  ErrorType.Forbidden,
114
+ ErrorType.NotFound,
115
+ ErrorType.RateLimit,
108
116
  ErrorType.Validation,
109
117
  ]),
110
118
  network: new Set([ErrorType.Network]),
@@ -600,6 +608,52 @@ export const DEPLOYMENT_CONFIG_FILENAME = 'ship.json';
600
608
  export const SPA_DEFAULT_CONFIG = {
601
609
  rewrites: [{ source: '/(.*)', destination: '/index.html' }],
602
610
  };
611
+ /**
612
+ * Assert that a ship.json file is *syntactically* loadable. Syntax only —
613
+ * never schema.
614
+ *
615
+ * ship.json is validated and compiled on the server, deliberately: the schema
616
+ * and the compiler evolve, and a client that judged them would reject configs
617
+ * a newer platform accepts. That reasoning bounds what a client may check to
618
+ * the properties which are true of *every* past and future schema:
619
+ *
620
+ * 1. it parses as JSON — JSON syntax is frozen (RFC 8259), so text that
621
+ * does not parse can never be a valid config;
622
+ * 2. its top level is an object — ship.json is `{ ... }` in every version.
623
+ *
624
+ * Both are monotonic: neither can ever reject something the server would
625
+ * accept. Everything beyond them (field names, types, rule semantics, which
626
+ * keys are permitted) stays server-side, where it can change.
627
+ *
628
+ * The payoff is the common case. Hand-edited JSON fails on a trailing comma,
629
+ * a `//` comment, single quotes, unquoted keys, or smart quotes pasted from
630
+ * documentation — mistakes that otherwise cost a full upload round-trip to
631
+ * discover. A UTF-8 BOM (Windows editors, PowerShell redirects) is stripped
632
+ * before parsing rather than rejected, because the server accepts it too;
633
+ * diverging there would reintroduce exactly the false rejection this
634
+ * function exists to avoid.
635
+ *
636
+ * @throws {ShipError} `ErrorType.Config` — the same type the server's own
637
+ * config rejection carries, so the error contract is identical wherever the
638
+ * failure is detected.
639
+ */
640
+ export function assertShipJsonSyntax(text) {
641
+ const withoutBom = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
642
+ let parsed;
643
+ try {
644
+ parsed = JSON.parse(withoutBom);
645
+ }
646
+ catch (error) {
647
+ throw ShipError.config(`invalid JSON format in config: ${error.message}`, {
648
+ filePath: DEPLOYMENT_CONFIG_FILENAME,
649
+ });
650
+ }
651
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
652
+ throw ShipError.config(`${DEPLOYMENT_CONFIG_FILENAME} must contain a JSON object`, {
653
+ filePath: DEPLOYMENT_CONFIG_FILENAME,
654
+ });
655
+ }
656
+ }
603
657
  // =============================================================================
604
658
  // VALIDATION UTILITIES
605
659
  // =============================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipstatic/types",
3
- "version": "2.5.0-beta.2",
3
+ "version": "2.5.0-beta.4",
4
4
  "description": "Shared types for ShipStatic platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -44,8 +44,8 @@
44
44
  },
45
45
  "devDependencies": {
46
46
  "@biomejs/biome": "2.5.5",
47
- "@types/node": "^24.10.9",
47
+ "@types/node": "^24.13.3",
48
48
  "typescript": "^5.9.3",
49
- "vitest": "^2.1.8"
49
+ "vitest": "^2.1.9"
50
50
  }
51
51
  }
package/src/index.ts CHANGED
@@ -229,6 +229,8 @@ export interface TokenListItem {
229
229
  export interface TokenListResponse {
230
230
  /** Array of tokens (security-redacted for list display) */
231
231
  tokens: TokenListItem[];
232
+ /** Cursor for pagination, null if no more pages */
233
+ cursor: string | null;
232
234
  /** Total number of tokens */
233
235
  total: number;
234
236
  }
@@ -397,11 +399,19 @@ const CLIENT_ONLY_ERROR_TYPES = new Set<string>([
397
399
  * union so `.has(error.type)` accepts any value from the union.
398
400
  */
399
401
  const ERROR_CATEGORIES = {
402
+ /**
403
+ * Every 4xx-class type — the caller's request or state is at fault, and
404
+ * the authored message is safe to surface verbatim. The set is exhaustive
405
+ * on purpose: a partial one forces consumers to add a status-range check
406
+ * beside every `isClientError()` call for the types it forgot.
407
+ */
400
408
  client: new Set<ErrorType>([
401
409
  ErrorType.Business,
402
410
  ErrorType.Config,
403
411
  ErrorType.File,
404
412
  ErrorType.Forbidden,
413
+ ErrorType.NotFound,
414
+ ErrorType.RateLimit,
405
415
  ErrorType.Validation,
406
416
  ]),
407
417
  network: new Set<ErrorType>([ErrorType.Network]),
@@ -1004,6 +1014,54 @@ export const SPA_DEFAULT_CONFIG = {
1004
1014
  rewrites: [{ source: '/(.*)', destination: '/index.html' }],
1005
1015
  } as const;
1006
1016
 
1017
+ /**
1018
+ * Assert that a ship.json file is *syntactically* loadable. Syntax only —
1019
+ * never schema.
1020
+ *
1021
+ * ship.json is validated and compiled on the server, deliberately: the schema
1022
+ * and the compiler evolve, and a client that judged them would reject configs
1023
+ * a newer platform accepts. That reasoning bounds what a client may check to
1024
+ * the properties which are true of *every* past and future schema:
1025
+ *
1026
+ * 1. it parses as JSON — JSON syntax is frozen (RFC 8259), so text that
1027
+ * does not parse can never be a valid config;
1028
+ * 2. its top level is an object — ship.json is `{ ... }` in every version.
1029
+ *
1030
+ * Both are monotonic: neither can ever reject something the server would
1031
+ * accept. Everything beyond them (field names, types, rule semantics, which
1032
+ * keys are permitted) stays server-side, where it can change.
1033
+ *
1034
+ * The payoff is the common case. Hand-edited JSON fails on a trailing comma,
1035
+ * a `//` comment, single quotes, unquoted keys, or smart quotes pasted from
1036
+ * documentation — mistakes that otherwise cost a full upload round-trip to
1037
+ * discover. A UTF-8 BOM (Windows editors, PowerShell redirects) is stripped
1038
+ * before parsing rather than rejected, because the server accepts it too;
1039
+ * diverging there would reintroduce exactly the false rejection this
1040
+ * function exists to avoid.
1041
+ *
1042
+ * @throws {ShipError} `ErrorType.Config` — the same type the server's own
1043
+ * config rejection carries, so the error contract is identical wherever the
1044
+ * failure is detected.
1045
+ */
1046
+ export function assertShipJsonSyntax(text: string): void {
1047
+ const withoutBom = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
1048
+
1049
+ let parsed: unknown;
1050
+ try {
1051
+ parsed = JSON.parse(withoutBom);
1052
+ } catch (error) {
1053
+ throw ShipError.config(`invalid JSON format in config: ${(error as Error).message}`, {
1054
+ filePath: DEPLOYMENT_CONFIG_FILENAME,
1055
+ });
1056
+ }
1057
+
1058
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
1059
+ throw ShipError.config(`${DEPLOYMENT_CONFIG_FILENAME} must contain a JSON object`, {
1060
+ filePath: DEPLOYMENT_CONFIG_FILENAME,
1061
+ });
1062
+ }
1063
+ }
1064
+
1007
1065
  // =============================================================================
1008
1066
  // VALIDATION UTILITIES
1009
1067
  // =============================================================================
@@ -1480,6 +1538,10 @@ export interface ActivityMeta {
1480
1538
  export interface ActivityListResponse {
1481
1539
  /** Array of activities */
1482
1540
  activities: Activity[];
1541
+ /** Cursor for pagination, null if no more pages */
1542
+ cursor: string | null;
1543
+ /** Total number of activities */
1544
+ total: number;
1483
1545
  }
1484
1546
 
1485
1547
  // =============================================================================