@shipstatic/types 2.5.0-beta.20 → 2.5.0-beta.22

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
@@ -79,62 +79,6 @@ export interface DeploymentCreateResponse extends Deployment {
79
79
  /** Claim URL for public deployments. Present when deployed without credentials. */
80
80
  readonly claim?: string;
81
81
  }
82
- /**
83
- * Every path the public API answers on, declared once.
84
- *
85
- * The URL surface was written out in four places — the API's mounts, the
86
- * SDK's client, the dashboard's client, and the post-deploy smoke — so a
87
- * rename meant finding all four. The first three now read this table.
88
- *
89
- * The smoke (`cloudflare/api/smoke.mjs`) deliberately still spells its own:
90
- * five of its nine paths are `/admin/*`, which this table excludes by
91
- * design, and splitting one list between a registry and literals reads worse
92
- * than keeping it uniform.
93
- *
94
- * **What this guarantees, exactly.** Collection paths are mounted from here,
95
- * so producer and consumer cannot diverge. Item paths are declared here and
96
- * consumed by clients, but the API spells them relative to their mount
97
- * (`/:deployment/config`), so the table does not *generate* them — it is
98
- * held to them by `api/tests/architecture/api-paths.test.ts`, which fails if
99
- * any entry names a path no route answers. Some entries have no client yet
100
- * (`DEPLOYMENT_CONFIG`, `DOMAIN_PROPAGATION` — endpoints the SDK
101
- * deliberately does not reach); the fence is what keeps those honest rather
102
- * than merely asserted.
103
- *
104
- * **The operator surface is deliberately absent.** `/admin/*` paths belong
105
- * to `web/my`, for the same reason its row types do: this package is
106
- * published, and the operator surface is not public (see `CLAUDE.md`, "Admin
107
- * types"). A path here is a promise to every npm consumer; `/admin` is a
108
- * promise to one dashboard.
109
- *
110
- * Item paths are functions rather than templates so the key is interpolated
111
- * in one place, encoded the same way by every caller.
112
- */
113
- export declare const API_PATHS: {
114
- readonly DEPLOYMENTS: "/deployments";
115
- readonly DEPLOYMENT: (deployment: string) => string;
116
- readonly DEPLOYMENT_CONFIG: (deployment: string) => string;
117
- readonly DOMAINS: "/domains";
118
- readonly DOMAIN: (domain: string) => string;
119
- readonly DOMAIN_VERIFY: (domain: string) => string;
120
- readonly DOMAIN_DNS: (domain: string) => string;
121
- readonly DOMAIN_RECORDS: (domain: string) => string;
122
- readonly DOMAIN_SHARE: (domain: string) => string;
123
- readonly DOMAIN_PROPAGATION: (domain: string) => string;
124
- readonly DOMAINS_VALIDATE: "/domains/validate";
125
- readonly TOKENS: "/tokens";
126
- readonly TOKEN: (token: string) => string;
127
- readonly ACCOUNT: "/account";
128
- readonly ACCOUNT_KEY: "/account/key";
129
- readonly ACCOUNT_CLAIM: "/account/claim";
130
- readonly ACTIVITIES: "/activities";
131
- readonly LABELS: "/labels";
132
- readonly LIMITS: "/limits";
133
- readonly PING: "/ping";
134
- readonly SETUP: "/setup";
135
- readonly SPA_CHECK: "/spa-check";
136
- readonly UPLOAD: "/upload";
137
- };
138
82
  /**
139
83
  * The half of a list response that is identical on every list.
140
84
  *
@@ -153,6 +97,28 @@ export interface ListResponse {
153
97
  /** Opaque cursor from this page; `null` on the last page. */
154
98
  cursor: string | null;
155
99
  }
100
+ /**
101
+ * Pagination options for every list endpoint. The response's `cursor` feeds
102
+ * the next request; a `null` cursor means the last page. Omitting both
103
+ * returns the server's default first page.
104
+ *
105
+ * A list answers `{ <collection>, cursor }` and nothing else — `cursor`
106
+ * carries the entire has-more signal, so no redundant boolean, and no
107
+ * `total`. **A count is an aggregate over a collection, not a property of a
108
+ * page:** including one makes every read pay for a full scan it did not ask
109
+ * for, which is precisely the cost keyset pagination exists to avoid.
110
+ *
111
+ * Counts therefore live on the summary resource that owns them —
112
+ * `GET /account` (`usage`) for a caller's own totals, `GET /admin/stats` for
113
+ * platform-wide ones. Ask for a count when you want a count; ask for a page
114
+ * when you want a page.
115
+ */
116
+ export interface ListOptions {
117
+ /** Maximum number of items to return in one page. */
118
+ limit?: number;
119
+ /** Opaque cursor from the previous page's response. */
120
+ cursor?: string;
121
+ }
156
122
  /**
157
123
  * Response for listing deployments
158
124
  */
@@ -623,6 +589,97 @@ export interface AccountOverrides {
623
589
  /** Override for maximum total deployment size in bytes */
624
590
  totalSize?: number;
625
591
  }
592
+ /**
593
+ * Every path the public API answers on, declared once.
594
+ *
595
+ * The URL surface was written out in four places — the API's mounts, the
596
+ * SDK's client, the dashboard's client, and the post-deploy smoke — so a
597
+ * rename meant finding all four. The first three now read this table.
598
+ *
599
+ * The smoke (`cloudflare/api/smoke.mjs`) deliberately still spells its own:
600
+ * five of its nine paths are `/admin/*`, which this table excludes by
601
+ * design, and splitting one list between a registry and literals reads worse
602
+ * than keeping it uniform.
603
+ *
604
+ * **What this guarantees, exactly.** Collection paths are mounted from here,
605
+ * so producer and consumer cannot diverge. Item paths are declared here and
606
+ * consumed by clients, but the API spells them relative to their mount
607
+ * (`/:deployment/config`), so the table does not *generate* them — it is
608
+ * held to them by `api/tests/architecture/api-paths.test.ts`, which fails if
609
+ * any entry names a path no route answers. Some entries have no client yet
610
+ * (`DEPLOYMENT_CONFIG`, `DOMAIN_PROPAGATION` — endpoints the SDK
611
+ * deliberately does not reach); the fence is what keeps those honest rather
612
+ * than merely asserted.
613
+ *
614
+ * **The operator surface is deliberately absent.** `/admin/*` paths belong
615
+ * to `web/my`, for the same reason its row types do: this package is
616
+ * published, and the operator surface is not public (see `CLAUDE.md`, "Admin
617
+ * types"). A path here is a promise to every npm consumer; `/admin` is a
618
+ * promise to one dashboard.
619
+ *
620
+ * Item paths are functions rather than templates so the key is interpolated
621
+ * in one place, encoded the same way by every caller.
622
+ */
623
+ export declare const API_PATHS: {
624
+ readonly DEPLOYMENTS: "/deployments";
625
+ readonly DEPLOYMENT: (deployment: string) => string;
626
+ readonly DEPLOYMENT_CONFIG: (deployment: string) => string;
627
+ readonly DOMAINS: "/domains";
628
+ readonly DOMAIN: (domain: string) => string;
629
+ readonly DOMAIN_VERIFY: (domain: string) => string;
630
+ readonly DOMAIN_DNS: (domain: string) => string;
631
+ readonly DOMAIN_RECORDS: (domain: string) => string;
632
+ readonly DOMAIN_SHARE: (domain: string) => string;
633
+ readonly DOMAIN_PROPAGATION: (domain: string) => string;
634
+ readonly DOMAINS_VALIDATE: "/domains/validate";
635
+ readonly TOKENS: "/tokens";
636
+ readonly TOKEN: (token: string) => string;
637
+ readonly ACCOUNT: "/account";
638
+ readonly ACCOUNT_KEY: "/account/key";
639
+ readonly ACCOUNT_CLAIM: "/account/claim";
640
+ readonly ACTIVITIES: "/activities";
641
+ readonly LABELS: "/labels";
642
+ readonly LIMITS: "/limits";
643
+ readonly PING: "/ping";
644
+ readonly SETUP: "/setup";
645
+ readonly SPA_CHECK: "/spa-check";
646
+ readonly UPLOAD: "/upload";
647
+ };
648
+ /**
649
+ * The deploy request's multipart field names — the other half of the wire
650
+ * surface beside {@link API_PATHS}. `POST /deployments` (and the first-party
651
+ * `/upload`) is multipart/form-data, and these are the names the API reads.
652
+ *
653
+ * Declared once because the body has three independent WRITERS — the SDK's
654
+ * Node and browser body builders, and the n8n community node's hand-rolled
655
+ * client (which cannot import this under n8n Cloud's zero-dependency rule,
656
+ * and fences its restated copy instead) — and until this export every writer
657
+ * restated the strings the API parses, with nothing comparing them.
658
+ *
659
+ * `FILES` carries one entry per file (the API reads it with `getAll`); every
660
+ * other field is single. The `@internal` flags are serialized as the literal
661
+ * string `'true'` and belong to first-party surfaces only.
662
+ */
663
+ export declare const DEPLOY_FIELDS: {
664
+ /** One entry per file — read with `getAll`. */
665
+ readonly FILES: "files[]";
666
+ /** JSON array of MD5 hex digests, index-aligned with `FILES`. */
667
+ readonly CHECKSUMS: "checksums";
668
+ /** JSON array of label strings. */
669
+ readonly LABELS: "labels";
670
+ /** The deploying surface's {@link DeploymentVia} member. */
671
+ readonly VIA: "via";
672
+ /** Plaintext password — the API hashes it server-side. */
673
+ readonly PASSWORD: "password";
674
+ /** @internal Server-processing flag — first-party `/upload` only. */
675
+ readonly BUILD: "build";
676
+ /** @internal Server-processing flag — first-party `/upload` only. */
677
+ readonly PRERENDER: "prerender";
678
+ /** @internal Server-processing flag — first-party `/upload` only. */
679
+ readonly SPA: "spa";
680
+ /** @internal reCAPTCHA proof — `web/www`'s public uploader only. */
681
+ readonly CAPTCHA: "captcha";
682
+ };
626
683
  /**
627
684
  * All possible error types in the ShipStatic platform.
628
685
  *
@@ -1023,6 +1080,26 @@ export declare const SPA_DEFAULT_CONFIG: {
1023
1080
  readonly destination: "/index.html";
1024
1081
  }];
1025
1082
  };
1083
+ /**
1084
+ * The `/spa-check` pre-flight's client-side envelope: which file is the
1085
+ * check's subject, and how large it may be before a client skips the call.
1086
+ *
1087
+ * One fact with three holders until this export — the API's config declared
1088
+ * the cap, the SDK's `checkSPA` hardcoded `100 * 1024`, and prose restated
1089
+ * "100KB". `INDEX_FILE` is the selection rule (the file whose content rides
1090
+ * `SPACheckRequest.index`), restated by every client that builds the request.
1091
+ *
1092
+ * Neither member is a validation boundary: a client over the cap simply
1093
+ * skips the pre-flight, because the server answers an oversized index
1094
+ * `isSPA: false` anyway. A consumer that cannot import this (n8n) needs no
1095
+ * size copy at all — outcome parity is the server's, not the client's.
1096
+ */
1097
+ export declare const SPA_CHECK_CONSTRAINTS: {
1098
+ /** The file whose content is the check's subject. */
1099
+ readonly INDEX_FILE: "index.html";
1100
+ /** Skip the pre-flight above this size — the server would answer false. */
1101
+ readonly MAX_INDEX_BYTES: number;
1102
+ };
1026
1103
  /**
1027
1104
  * Assert that a ship.json file is *syntactically* loadable. Syntax only —
1028
1105
  * never schema.
@@ -1147,6 +1224,35 @@ export interface StaticFile {
1147
1224
  }
1148
1225
  /** Default API URL if not otherwise configured. */
1149
1226
  export declare const DEFAULT_API = "https://api.shipstatic.com";
1227
+ /**
1228
+ * The Node SDK's ambient configuration pair — the ONLY environment variables
1229
+ * the SDK reads, and therefore the COMPLETE list an embedding host must
1230
+ * scrub (per `npm/ship`'s strict-isolation contract, scrubbing is the host's
1231
+ * job, not the SDK's). A host that derives its scrub from this object's
1232
+ * values — as the VS Code extension's child-process env block does — picks
1233
+ * up a grown contract at the next pin bump instead of by remembered prose.
1234
+ *
1235
+ * Browser builds read no environment at all, and the CLI-only variables
1236
+ * (`SHIP_PASSWORD`, `SHIP_VIA`) are deliberately NOT here: they are the
1237
+ * CLI's operational levers, not the SDK's ambient contract — see
1238
+ * `npm/ship/CLAUDE.md`, "CLI-only env vars".
1239
+ */
1240
+ export declare const SHIP_ENV: {
1241
+ /** The one credential slot — any platform token. */
1242
+ readonly TOKEN: "SHIP_TOKEN";
1243
+ /** The API endpoint override. */
1244
+ readonly API_URL: "SHIP_API_URL";
1245
+ };
1246
+ /**
1247
+ * Where a human creates an API key — the console deep link quoted by every
1248
+ * surface that teaches authentication (the CLI's config wizard, the VS Code
1249
+ * and n8n listings, the n8n rate-limit hint and credential copy). Written
1250
+ * out in five files across three repos until this export.
1251
+ *
1252
+ * Production-branded by design: published artifacts name the product, never
1253
+ * an environment (root `CLAUDE.md`, "Environment-Aware URLs").
1254
+ */
1255
+ export declare const MY_API_KEY_URL = "https://my.shipstatic.com/api-key";
1150
1256
  /**
1151
1257
  * How long an anonymous deployment lives before it expires.
1152
1258
  *
@@ -1225,28 +1331,6 @@ export interface DeploymentUploadOptions {
1225
1331
  */
1226
1332
  idempotencyKey?: string;
1227
1333
  }
1228
- /**
1229
- * Pagination options for every list endpoint. The response's `cursor` feeds
1230
- * the next request; a `null` cursor means the last page. Omitting both
1231
- * returns the server's default first page.
1232
- *
1233
- * A list answers `{ <collection>, cursor }` and nothing else — `cursor`
1234
- * carries the entire has-more signal, so no redundant boolean, and no
1235
- * `total`. **A count is an aggregate over a collection, not a property of a
1236
- * page:** including one makes every read pay for a full scan it did not ask
1237
- * for, which is precisely the cost keyset pagination exists to avoid.
1238
- *
1239
- * Counts therefore live on the summary resource that owns them —
1240
- * `GET /account` (`usage`) for a caller's own totals, `GET /admin/stats` for
1241
- * platform-wide ones. Ask for a count when you want a count; ask for a page
1242
- * when you want a page.
1243
- */
1244
- export interface ListOptions {
1245
- /** Maximum number of items to return in one page. */
1246
- limit?: number;
1247
- /** Opaque cursor from the previous page's response. */
1248
- cursor?: string;
1249
- }
1250
1334
  /**
1251
1335
  * What a caller may change on an existing deployment.
1252
1336
  *
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * This package is the single source of truth for all shared data structures.
4
4
  */
5
5
  // =============================================================================
6
- // I. CORE ENTITIES
6
+ // DEPLOYMENT TYPES
7
7
  // =============================================================================
8
8
  /**
9
9
  * Deployment status constants
@@ -35,62 +35,6 @@ export const DeploymentVia = {
35
35
  GPT: 'gpt',
36
36
  VSC: 'vsc',
37
37
  };
38
- /**
39
- * Every path the public API answers on, declared once.
40
- *
41
- * The URL surface was written out in four places — the API's mounts, the
42
- * SDK's client, the dashboard's client, and the post-deploy smoke — so a
43
- * rename meant finding all four. The first three now read this table.
44
- *
45
- * The smoke (`cloudflare/api/smoke.mjs`) deliberately still spells its own:
46
- * five of its nine paths are `/admin/*`, which this table excludes by
47
- * design, and splitting one list between a registry and literals reads worse
48
- * than keeping it uniform.
49
- *
50
- * **What this guarantees, exactly.** Collection paths are mounted from here,
51
- * so producer and consumer cannot diverge. Item paths are declared here and
52
- * consumed by clients, but the API spells them relative to their mount
53
- * (`/:deployment/config`), so the table does not *generate* them — it is
54
- * held to them by `api/tests/architecture/api-paths.test.ts`, which fails if
55
- * any entry names a path no route answers. Some entries have no client yet
56
- * (`DEPLOYMENT_CONFIG`, `DOMAIN_PROPAGATION` — endpoints the SDK
57
- * deliberately does not reach); the fence is what keeps those honest rather
58
- * than merely asserted.
59
- *
60
- * **The operator surface is deliberately absent.** `/admin/*` paths belong
61
- * to `web/my`, for the same reason its row types do: this package is
62
- * published, and the operator surface is not public (see `CLAUDE.md`, "Admin
63
- * types"). A path here is a promise to every npm consumer; `/admin` is a
64
- * promise to one dashboard.
65
- *
66
- * Item paths are functions rather than templates so the key is interpolated
67
- * in one place, encoded the same way by every caller.
68
- */
69
- export const API_PATHS = {
70
- DEPLOYMENTS: '/deployments',
71
- DEPLOYMENT: (deployment) => `/deployments/${deployment}`,
72
- DEPLOYMENT_CONFIG: (deployment) => `/deployments/${deployment}/config`,
73
- DOMAINS: '/domains',
74
- DOMAIN: (domain) => `/domains/${domain}`,
75
- DOMAIN_VERIFY: (domain) => `/domains/${domain}/verify`,
76
- DOMAIN_DNS: (domain) => `/domains/${domain}/dns`,
77
- DOMAIN_RECORDS: (domain) => `/domains/${domain}/records`,
78
- DOMAIN_SHARE: (domain) => `/domains/${domain}/share`,
79
- DOMAIN_PROPAGATION: (domain) => `/domains/${domain}/propagation`,
80
- DOMAINS_VALIDATE: '/domains/validate',
81
- TOKENS: '/tokens',
82
- TOKEN: (token) => `/tokens/${token}`,
83
- ACCOUNT: '/account',
84
- ACCOUNT_KEY: '/account/key',
85
- ACCOUNT_CLAIM: '/account/claim',
86
- ACTIVITIES: '/activities',
87
- LABELS: '/labels',
88
- LIMITS: '/limits',
89
- PING: '/ping',
90
- SETUP: '/setup',
91
- SPA_CHECK: '/spa-check',
92
- UPLOAD: '/upload',
93
- };
94
38
  // =============================================================================
95
39
  // DOMAIN TYPES
96
40
  // =============================================================================
@@ -186,6 +130,100 @@ export const AccountPlan = {
186
130
  TERMINATED: 'terminated',
187
131
  };
188
132
  // =============================================================================
133
+ // WIRE SURFACE
134
+ // =============================================================================
135
+ /**
136
+ * Every path the public API answers on, declared once.
137
+ *
138
+ * The URL surface was written out in four places — the API's mounts, the
139
+ * SDK's client, the dashboard's client, and the post-deploy smoke — so a
140
+ * rename meant finding all four. The first three now read this table.
141
+ *
142
+ * The smoke (`cloudflare/api/smoke.mjs`) deliberately still spells its own:
143
+ * five of its nine paths are `/admin/*`, which this table excludes by
144
+ * design, and splitting one list between a registry and literals reads worse
145
+ * than keeping it uniform.
146
+ *
147
+ * **What this guarantees, exactly.** Collection paths are mounted from here,
148
+ * so producer and consumer cannot diverge. Item paths are declared here and
149
+ * consumed by clients, but the API spells them relative to their mount
150
+ * (`/:deployment/config`), so the table does not *generate* them — it is
151
+ * held to them by `api/tests/architecture/api-paths.test.ts`, which fails if
152
+ * any entry names a path no route answers. Some entries have no client yet
153
+ * (`DEPLOYMENT_CONFIG`, `DOMAIN_PROPAGATION` — endpoints the SDK
154
+ * deliberately does not reach); the fence is what keeps those honest rather
155
+ * than merely asserted.
156
+ *
157
+ * **The operator surface is deliberately absent.** `/admin/*` paths belong
158
+ * to `web/my`, for the same reason its row types do: this package is
159
+ * published, and the operator surface is not public (see `CLAUDE.md`, "Admin
160
+ * types"). A path here is a promise to every npm consumer; `/admin` is a
161
+ * promise to one dashboard.
162
+ *
163
+ * Item paths are functions rather than templates so the key is interpolated
164
+ * in one place, encoded the same way by every caller.
165
+ */
166
+ export const API_PATHS = {
167
+ DEPLOYMENTS: '/deployments',
168
+ DEPLOYMENT: (deployment) => `/deployments/${deployment}`,
169
+ DEPLOYMENT_CONFIG: (deployment) => `/deployments/${deployment}/config`,
170
+ DOMAINS: '/domains',
171
+ DOMAIN: (domain) => `/domains/${domain}`,
172
+ DOMAIN_VERIFY: (domain) => `/domains/${domain}/verify`,
173
+ DOMAIN_DNS: (domain) => `/domains/${domain}/dns`,
174
+ DOMAIN_RECORDS: (domain) => `/domains/${domain}/records`,
175
+ DOMAIN_SHARE: (domain) => `/domains/${domain}/share`,
176
+ DOMAIN_PROPAGATION: (domain) => `/domains/${domain}/propagation`,
177
+ DOMAINS_VALIDATE: '/domains/validate',
178
+ TOKENS: '/tokens',
179
+ TOKEN: (token) => `/tokens/${token}`,
180
+ ACCOUNT: '/account',
181
+ ACCOUNT_KEY: '/account/key',
182
+ ACCOUNT_CLAIM: '/account/claim',
183
+ ACTIVITIES: '/activities',
184
+ LABELS: '/labels',
185
+ LIMITS: '/limits',
186
+ PING: '/ping',
187
+ SETUP: '/setup',
188
+ SPA_CHECK: '/spa-check',
189
+ UPLOAD: '/upload',
190
+ };
191
+ /**
192
+ * The deploy request's multipart field names — the other half of the wire
193
+ * surface beside {@link API_PATHS}. `POST /deployments` (and the first-party
194
+ * `/upload`) is multipart/form-data, and these are the names the API reads.
195
+ *
196
+ * Declared once because the body has three independent WRITERS — the SDK's
197
+ * Node and browser body builders, and the n8n community node's hand-rolled
198
+ * client (which cannot import this under n8n Cloud's zero-dependency rule,
199
+ * and fences its restated copy instead) — and until this export every writer
200
+ * restated the strings the API parses, with nothing comparing them.
201
+ *
202
+ * `FILES` carries one entry per file (the API reads it with `getAll`); every
203
+ * other field is single. The `@internal` flags are serialized as the literal
204
+ * string `'true'` and belong to first-party surfaces only.
205
+ */
206
+ export const DEPLOY_FIELDS = {
207
+ /** One entry per file — read with `getAll`. */
208
+ FILES: 'files[]',
209
+ /** JSON array of MD5 hex digests, index-aligned with `FILES`. */
210
+ CHECKSUMS: 'checksums',
211
+ /** JSON array of label strings. */
212
+ LABELS: 'labels',
213
+ /** The deploying surface's {@link DeploymentVia} member. */
214
+ VIA: 'via',
215
+ /** Plaintext password — the API hashes it server-side. */
216
+ PASSWORD: 'password',
217
+ /** @internal Server-processing flag — first-party `/upload` only. */
218
+ BUILD: 'build',
219
+ /** @internal Server-processing flag — first-party `/upload` only. */
220
+ PRERENDER: 'prerender',
221
+ /** @internal Server-processing flag — first-party `/upload` only. */
222
+ SPA: 'spa',
223
+ /** @internal reCAPTCHA proof — `web/www`'s public uploader only. */
224
+ CAPTCHA: 'captcha',
225
+ };
226
+ // =============================================================================
189
227
  // ERROR SYSTEM
190
228
  // =============================================================================
191
229
  /**
@@ -948,6 +986,26 @@ export const DEPLOYMENT_CONFIG_FILENAME = 'ship.json';
948
986
  export const SPA_DEFAULT_CONFIG = {
949
987
  rewrites: [{ source: '/(.*)', destination: '/index.html' }],
950
988
  };
989
+ /**
990
+ * The `/spa-check` pre-flight's client-side envelope: which file is the
991
+ * check's subject, and how large it may be before a client skips the call.
992
+ *
993
+ * One fact with three holders until this export — the API's config declared
994
+ * the cap, the SDK's `checkSPA` hardcoded `100 * 1024`, and prose restated
995
+ * "100KB". `INDEX_FILE` is the selection rule (the file whose content rides
996
+ * `SPACheckRequest.index`), restated by every client that builds the request.
997
+ *
998
+ * Neither member is a validation boundary: a client over the cap simply
999
+ * skips the pre-flight, because the server answers an oversized index
1000
+ * `isSPA: false` anyway. A consumer that cannot import this (n8n) needs no
1001
+ * size copy at all — outcome parity is the server's, not the client's.
1002
+ */
1003
+ export const SPA_CHECK_CONSTRAINTS = {
1004
+ /** The file whose content is the check's subject. */
1005
+ INDEX_FILE: 'index.html',
1006
+ /** Skip the pre-flight above this size — the server would answer false. */
1007
+ MAX_INDEX_BYTES: 100 * 1024,
1008
+ };
951
1009
  /**
952
1010
  * Assert that a ship.json file is *syntactically* loadable. Syntax only —
953
1011
  * never schema.
@@ -1090,6 +1148,35 @@ export function isDeployment(input) {
1090
1148
  // =============================================================================
1091
1149
  /** Default API URL if not otherwise configured. */
1092
1150
  export const DEFAULT_API = 'https://api.shipstatic.com';
1151
+ /**
1152
+ * The Node SDK's ambient configuration pair — the ONLY environment variables
1153
+ * the SDK reads, and therefore the COMPLETE list an embedding host must
1154
+ * scrub (per `npm/ship`'s strict-isolation contract, scrubbing is the host's
1155
+ * job, not the SDK's). A host that derives its scrub from this object's
1156
+ * values — as the VS Code extension's child-process env block does — picks
1157
+ * up a grown contract at the next pin bump instead of by remembered prose.
1158
+ *
1159
+ * Browser builds read no environment at all, and the CLI-only variables
1160
+ * (`SHIP_PASSWORD`, `SHIP_VIA`) are deliberately NOT here: they are the
1161
+ * CLI's operational levers, not the SDK's ambient contract — see
1162
+ * `npm/ship/CLAUDE.md`, "CLI-only env vars".
1163
+ */
1164
+ export const SHIP_ENV = {
1165
+ /** The one credential slot — any platform token. */
1166
+ TOKEN: 'SHIP_TOKEN',
1167
+ /** The API endpoint override. */
1168
+ API_URL: 'SHIP_API_URL',
1169
+ };
1170
+ /**
1171
+ * Where a human creates an API key — the console deep link quoted by every
1172
+ * surface that teaches authentication (the CLI's config wizard, the VS Code
1173
+ * and n8n listings, the n8n rate-limit hint and credential copy). Written
1174
+ * out in five files across three repos until this export.
1175
+ *
1176
+ * Production-branded by design: published artifacts name the product, never
1177
+ * an environment (root `CLAUDE.md`, "Environment-Aware URLs").
1178
+ */
1179
+ export const MY_API_KEY_URL = 'https://my.shipstatic.com/api-key';
1093
1180
  /**
1094
1181
  * How long an anonymous deployment lives before it expires.
1095
1182
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipstatic/types",
3
- "version": "2.5.0-beta.20",
3
+ "version": "2.5.0-beta.22",
4
4
  "description": "Shared types for ShipStatic platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  // =============================================================================
7
- // I. CORE ENTITIES
7
+ // DEPLOYMENT TYPES
8
8
  // =============================================================================
9
9
 
10
10
  /**
@@ -90,63 +90,6 @@ export interface DeploymentCreateResponse extends Deployment {
90
90
  readonly claim?: string;
91
91
  }
92
92
 
93
- /**
94
- * Every path the public API answers on, declared once.
95
- *
96
- * The URL surface was written out in four places — the API's mounts, the
97
- * SDK's client, the dashboard's client, and the post-deploy smoke — so a
98
- * rename meant finding all four. The first three now read this table.
99
- *
100
- * The smoke (`cloudflare/api/smoke.mjs`) deliberately still spells its own:
101
- * five of its nine paths are `/admin/*`, which this table excludes by
102
- * design, and splitting one list between a registry and literals reads worse
103
- * than keeping it uniform.
104
- *
105
- * **What this guarantees, exactly.** Collection paths are mounted from here,
106
- * so producer and consumer cannot diverge. Item paths are declared here and
107
- * consumed by clients, but the API spells them relative to their mount
108
- * (`/:deployment/config`), so the table does not *generate* them — it is
109
- * held to them by `api/tests/architecture/api-paths.test.ts`, which fails if
110
- * any entry names a path no route answers. Some entries have no client yet
111
- * (`DEPLOYMENT_CONFIG`, `DOMAIN_PROPAGATION` — endpoints the SDK
112
- * deliberately does not reach); the fence is what keeps those honest rather
113
- * than merely asserted.
114
- *
115
- * **The operator surface is deliberately absent.** `/admin/*` paths belong
116
- * to `web/my`, for the same reason its row types do: this package is
117
- * published, and the operator surface is not public (see `CLAUDE.md`, "Admin
118
- * types"). A path here is a promise to every npm consumer; `/admin` is a
119
- * promise to one dashboard.
120
- *
121
- * Item paths are functions rather than templates so the key is interpolated
122
- * in one place, encoded the same way by every caller.
123
- */
124
- export const API_PATHS = {
125
- DEPLOYMENTS: '/deployments',
126
- DEPLOYMENT: (deployment: string) => `/deployments/${deployment}`,
127
- DEPLOYMENT_CONFIG: (deployment: string) => `/deployments/${deployment}/config`,
128
- DOMAINS: '/domains',
129
- DOMAIN: (domain: string) => `/domains/${domain}`,
130
- DOMAIN_VERIFY: (domain: string) => `/domains/${domain}/verify`,
131
- DOMAIN_DNS: (domain: string) => `/domains/${domain}/dns`,
132
- DOMAIN_RECORDS: (domain: string) => `/domains/${domain}/records`,
133
- DOMAIN_SHARE: (domain: string) => `/domains/${domain}/share`,
134
- DOMAIN_PROPAGATION: (domain: string) => `/domains/${domain}/propagation`,
135
- DOMAINS_VALIDATE: '/domains/validate',
136
- TOKENS: '/tokens',
137
- TOKEN: (token: string) => `/tokens/${token}`,
138
- ACCOUNT: '/account',
139
- ACCOUNT_KEY: '/account/key',
140
- ACCOUNT_CLAIM: '/account/claim',
141
- ACTIVITIES: '/activities',
142
- LABELS: '/labels',
143
- LIMITS: '/limits',
144
- PING: '/ping',
145
- SETUP: '/setup',
146
- SPA_CHECK: '/spa-check',
147
- UPLOAD: '/upload',
148
- } as const;
149
-
150
93
  /**
151
94
  * The half of a list response that is identical on every list.
152
95
  *
@@ -166,6 +109,29 @@ export interface ListResponse {
166
109
  cursor: string | null;
167
110
  }
168
111
 
112
+ /**
113
+ * Pagination options for every list endpoint. The response's `cursor` feeds
114
+ * the next request; a `null` cursor means the last page. Omitting both
115
+ * returns the server's default first page.
116
+ *
117
+ * A list answers `{ <collection>, cursor }` and nothing else — `cursor`
118
+ * carries the entire has-more signal, so no redundant boolean, and no
119
+ * `total`. **A count is an aggregate over a collection, not a property of a
120
+ * page:** including one makes every read pay for a full scan it did not ask
121
+ * for, which is precisely the cost keyset pagination exists to avoid.
122
+ *
123
+ * Counts therefore live on the summary resource that owns them —
124
+ * `GET /account` (`usage`) for a caller's own totals, `GET /admin/stats` for
125
+ * platform-wide ones. Ask for a count when you want a count; ask for a page
126
+ * when you want a page.
127
+ */
128
+ export interface ListOptions {
129
+ /** Maximum number of items to return in one page. */
130
+ limit?: number;
131
+ /** Opaque cursor from the previous page's response. */
132
+ cursor?: string;
133
+ }
134
+
169
135
  /**
170
136
  * Response for listing deployments
171
137
  */
@@ -703,6 +669,103 @@ export interface AccountOverrides {
703
669
  totalSize?: number;
704
670
  }
705
671
 
672
+ // =============================================================================
673
+ // WIRE SURFACE
674
+ // =============================================================================
675
+
676
+ /**
677
+ * Every path the public API answers on, declared once.
678
+ *
679
+ * The URL surface was written out in four places — the API's mounts, the
680
+ * SDK's client, the dashboard's client, and the post-deploy smoke — so a
681
+ * rename meant finding all four. The first three now read this table.
682
+ *
683
+ * The smoke (`cloudflare/api/smoke.mjs`) deliberately still spells its own:
684
+ * five of its nine paths are `/admin/*`, which this table excludes by
685
+ * design, and splitting one list between a registry and literals reads worse
686
+ * than keeping it uniform.
687
+ *
688
+ * **What this guarantees, exactly.** Collection paths are mounted from here,
689
+ * so producer and consumer cannot diverge. Item paths are declared here and
690
+ * consumed by clients, but the API spells them relative to their mount
691
+ * (`/:deployment/config`), so the table does not *generate* them — it is
692
+ * held to them by `api/tests/architecture/api-paths.test.ts`, which fails if
693
+ * any entry names a path no route answers. Some entries have no client yet
694
+ * (`DEPLOYMENT_CONFIG`, `DOMAIN_PROPAGATION` — endpoints the SDK
695
+ * deliberately does not reach); the fence is what keeps those honest rather
696
+ * than merely asserted.
697
+ *
698
+ * **The operator surface is deliberately absent.** `/admin/*` paths belong
699
+ * to `web/my`, for the same reason its row types do: this package is
700
+ * published, and the operator surface is not public (see `CLAUDE.md`, "Admin
701
+ * types"). A path here is a promise to every npm consumer; `/admin` is a
702
+ * promise to one dashboard.
703
+ *
704
+ * Item paths are functions rather than templates so the key is interpolated
705
+ * in one place, encoded the same way by every caller.
706
+ */
707
+ export const API_PATHS = {
708
+ DEPLOYMENTS: '/deployments',
709
+ DEPLOYMENT: (deployment: string) => `/deployments/${deployment}`,
710
+ DEPLOYMENT_CONFIG: (deployment: string) => `/deployments/${deployment}/config`,
711
+ DOMAINS: '/domains',
712
+ DOMAIN: (domain: string) => `/domains/${domain}`,
713
+ DOMAIN_VERIFY: (domain: string) => `/domains/${domain}/verify`,
714
+ DOMAIN_DNS: (domain: string) => `/domains/${domain}/dns`,
715
+ DOMAIN_RECORDS: (domain: string) => `/domains/${domain}/records`,
716
+ DOMAIN_SHARE: (domain: string) => `/domains/${domain}/share`,
717
+ DOMAIN_PROPAGATION: (domain: string) => `/domains/${domain}/propagation`,
718
+ DOMAINS_VALIDATE: '/domains/validate',
719
+ TOKENS: '/tokens',
720
+ TOKEN: (token: string) => `/tokens/${token}`,
721
+ ACCOUNT: '/account',
722
+ ACCOUNT_KEY: '/account/key',
723
+ ACCOUNT_CLAIM: '/account/claim',
724
+ ACTIVITIES: '/activities',
725
+ LABELS: '/labels',
726
+ LIMITS: '/limits',
727
+ PING: '/ping',
728
+ SETUP: '/setup',
729
+ SPA_CHECK: '/spa-check',
730
+ UPLOAD: '/upload',
731
+ } as const;
732
+
733
+ /**
734
+ * The deploy request's multipart field names — the other half of the wire
735
+ * surface beside {@link API_PATHS}. `POST /deployments` (and the first-party
736
+ * `/upload`) is multipart/form-data, and these are the names the API reads.
737
+ *
738
+ * Declared once because the body has three independent WRITERS — the SDK's
739
+ * Node and browser body builders, and the n8n community node's hand-rolled
740
+ * client (which cannot import this under n8n Cloud's zero-dependency rule,
741
+ * and fences its restated copy instead) — and until this export every writer
742
+ * restated the strings the API parses, with nothing comparing them.
743
+ *
744
+ * `FILES` carries one entry per file (the API reads it with `getAll`); every
745
+ * other field is single. The `@internal` flags are serialized as the literal
746
+ * string `'true'` and belong to first-party surfaces only.
747
+ */
748
+ export const DEPLOY_FIELDS = {
749
+ /** One entry per file — read with `getAll`. */
750
+ FILES: 'files[]',
751
+ /** JSON array of MD5 hex digests, index-aligned with `FILES`. */
752
+ CHECKSUMS: 'checksums',
753
+ /** JSON array of label strings. */
754
+ LABELS: 'labels',
755
+ /** The deploying surface's {@link DeploymentVia} member. */
756
+ VIA: 'via',
757
+ /** Plaintext password — the API hashes it server-side. */
758
+ PASSWORD: 'password',
759
+ /** @internal Server-processing flag — first-party `/upload` only. */
760
+ BUILD: 'build',
761
+ /** @internal Server-processing flag — first-party `/upload` only. */
762
+ PRERENDER: 'prerender',
763
+ /** @internal Server-processing flag — first-party `/upload` only. */
764
+ SPA: 'spa',
765
+ /** @internal reCAPTCHA proof — `web/www`'s public uploader only. */
766
+ CAPTCHA: 'captcha',
767
+ } as const;
768
+
706
769
  // =============================================================================
707
770
  // ERROR SYSTEM
708
771
  // =============================================================================
@@ -1143,7 +1206,7 @@ export function isShipError(error: unknown): error is ShipError {
1143
1206
  }
1144
1207
 
1145
1208
  // =============================================================================
1146
- // CONFIG TYPES
1209
+ // PLATFORM LIMITS
1147
1210
  // =============================================================================
1148
1211
 
1149
1212
  /**
@@ -1593,6 +1656,27 @@ export const SPA_DEFAULT_CONFIG = {
1593
1656
  rewrites: [{ source: '/(.*)', destination: '/index.html' }],
1594
1657
  } as const;
1595
1658
 
1659
+ /**
1660
+ * The `/spa-check` pre-flight's client-side envelope: which file is the
1661
+ * check's subject, and how large it may be before a client skips the call.
1662
+ *
1663
+ * One fact with three holders until this export — the API's config declared
1664
+ * the cap, the SDK's `checkSPA` hardcoded `100 * 1024`, and prose restated
1665
+ * "100KB". `INDEX_FILE` is the selection rule (the file whose content rides
1666
+ * `SPACheckRequest.index`), restated by every client that builds the request.
1667
+ *
1668
+ * Neither member is a validation boundary: a client over the cap simply
1669
+ * skips the pre-flight, because the server answers an oversized index
1670
+ * `isSPA: false` anyway. A consumer that cannot import this (n8n) needs no
1671
+ * size copy at all — outcome parity is the server's, not the client's.
1672
+ */
1673
+ export const SPA_CHECK_CONSTRAINTS = {
1674
+ /** The file whose content is the check's subject. */
1675
+ INDEX_FILE: 'index.html',
1676
+ /** Skip the pre-flight above this size — the server would answer false. */
1677
+ MAX_INDEX_BYTES: 100 * 1024,
1678
+ } as const;
1679
+
1596
1680
  /**
1597
1681
  * Assert that a ship.json file is *syntactically* loadable. Syntax only —
1598
1682
  * never schema.
@@ -1834,6 +1918,37 @@ export interface StaticFile {
1834
1918
  /** Default API URL if not otherwise configured. */
1835
1919
  export const DEFAULT_API = 'https://api.shipstatic.com';
1836
1920
 
1921
+ /**
1922
+ * The Node SDK's ambient configuration pair — the ONLY environment variables
1923
+ * the SDK reads, and therefore the COMPLETE list an embedding host must
1924
+ * scrub (per `npm/ship`'s strict-isolation contract, scrubbing is the host's
1925
+ * job, not the SDK's). A host that derives its scrub from this object's
1926
+ * values — as the VS Code extension's child-process env block does — picks
1927
+ * up a grown contract at the next pin bump instead of by remembered prose.
1928
+ *
1929
+ * Browser builds read no environment at all, and the CLI-only variables
1930
+ * (`SHIP_PASSWORD`, `SHIP_VIA`) are deliberately NOT here: they are the
1931
+ * CLI's operational levers, not the SDK's ambient contract — see
1932
+ * `npm/ship/CLAUDE.md`, "CLI-only env vars".
1933
+ */
1934
+ export const SHIP_ENV = {
1935
+ /** The one credential slot — any platform token. */
1936
+ TOKEN: 'SHIP_TOKEN',
1937
+ /** The API endpoint override. */
1938
+ API_URL: 'SHIP_API_URL',
1939
+ } as const;
1940
+
1941
+ /**
1942
+ * Where a human creates an API key — the console deep link quoted by every
1943
+ * surface that teaches authentication (the CLI's config wizard, the VS Code
1944
+ * and n8n listings, the n8n rate-limit hint and credential copy). Written
1945
+ * out in five files across three repos until this export.
1946
+ *
1947
+ * Production-branded by design: published artifacts name the product, never
1948
+ * an environment (root `CLAUDE.md`, "Environment-Aware URLs").
1949
+ */
1950
+ export const MY_API_KEY_URL = 'https://my.shipstatic.com/api-key';
1951
+
1837
1952
  /**
1838
1953
  * How long an anonymous deployment lives before it expires.
1839
1954
  *
@@ -1919,29 +2034,6 @@ export interface DeploymentUploadOptions {
1919
2034
  idempotencyKey?: string;
1920
2035
  }
1921
2036
 
1922
- /**
1923
- * Pagination options for every list endpoint. The response's `cursor` feeds
1924
- * the next request; a `null` cursor means the last page. Omitting both
1925
- * returns the server's default first page.
1926
- *
1927
- * A list answers `{ <collection>, cursor }` and nothing else — `cursor`
1928
- * carries the entire has-more signal, so no redundant boolean, and no
1929
- * `total`. **A count is an aggregate over a collection, not a property of a
1930
- * page:** including one makes every read pay for a full scan it did not ask
1931
- * for, which is precisely the cost keyset pagination exists to avoid.
1932
- *
1933
- * Counts therefore live on the summary resource that owns them —
1934
- * `GET /account` (`usage`) for a caller's own totals, `GET /admin/stats` for
1935
- * platform-wide ones. Ask for a count when you want a count; ask for a page
1936
- * when you want a page.
1937
- */
1938
- export interface ListOptions {
1939
- /** Maximum number of items to return in one page. */
1940
- limit?: number;
1941
- /** Opaque cursor from the previous page's response. */
1942
- cursor?: string;
1943
- }
1944
-
1945
2037
  /**
1946
2038
  * What a caller may change on an existing deployment.
1947
2039
  *