@shipstatic/ship 0.1.26 → 0.2.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
@@ -1,6 +1,7 @@
1
1
  import * as _shipstatic_types from '@shipstatic/types';
2
- import { Deployment, DeploymentListResponse, Alias, AliasListResponse, Account } from '@shipstatic/types';
3
- export { PingResponse, ShipError, ShipErrorType } from '@shipstatic/types';
2
+ import { PingResponse, ConfigResponse, StaticFile, Deployment, DeploymentListResponse, Alias, AliasListResponse, Account, DeployInput, DeploymentResource, AliasResource, AccountResource, KeysResource, PlatformConfig } from '@shipstatic/types';
3
+ export * from '@shipstatic/types';
4
+ export { Account, Alias, DEFAULT_API, DeployInput, Deployment, PingResponse, ShipError, ShipErrorType } from '@shipstatic/types';
4
5
 
5
6
  /**
6
7
  * @file SDK-specific type definitions
@@ -8,10 +9,6 @@ export { PingResponse, ShipError, ShipErrorType } from '@shipstatic/types';
8
9
  * Core types come from @shipstatic/types, while SDK-specific types are defined here.
9
10
  */
10
11
 
11
- /**
12
- * Consolidated input type for all environments
13
- */
14
- type DeployInput = FileList | File[] | HTMLInputElement | string[];
15
12
  /**
16
13
  * Universal deploy options for both Node.js and Browser environments
17
14
  */
@@ -87,92 +84,282 @@ interface ShipClientOptions {
87
84
  * Used if an deploy operation doesn't specify its own `maxConcurrency`.
88
85
  * Defaults to 4 if not set here or in the specific deploy call.
89
86
  */
90
- maxConcurrentDeploys?: number | undefined;
87
+ maxConcurrency?: number | undefined;
91
88
  /**
92
89
  * Default timeout in milliseconds for API requests made by this client instance.
93
90
  * Used if an deploy operation doesn't specify its own timeout.
94
91
  */
95
92
  timeout?: number | undefined;
96
93
  }
94
+
97
95
  /**
98
- * Represents a file that has been processed and is ready for deploy.
99
- * Used internally by the SDK and in advanced/manual deploy scenarios.
96
+ * Handles direct HTTP communication with the Ship API, including deploys and health checks.
97
+ * Responsible for constructing requests, managing authentication, and error translation.
98
+ * @internal
100
99
  */
101
- interface StaticFile {
100
+ declare class ApiHttp {
101
+ #private;
102
+ private readonly apiUrl;
103
+ private readonly apiKey;
104
+ private readonly deployToken;
102
105
  /**
103
- * The content of the file.
104
- * In Node.js, this is typically a `Buffer`.
105
- * In the browser, this is typically a `File` or `Blob` object.
106
+ * Constructs a new ApiHttp instance with the provided client options.
107
+ * @param options - Client options including API host, authentication credentials, and timeout settings.
106
108
  */
107
- content: File | Buffer | Blob;
109
+ constructor(options: ShipClientOptions);
108
110
  /**
109
- * The desired path for the file on the server, relative to the deployment root.
110
- * Should include the filename, e.g., `images/photo.jpg`.
111
+ * Sends a ping request to the Ship API server to verify connectivity and authentication.
112
+ * @returns Promise resolving to `true` if the ping is successful, `false` otherwise.
113
+ * @throws {ShipApiError} If the API returns an error response (4xx, 5xx).
114
+ * @throws {ShipNetworkError} If a network error occurs (e.g., DNS failure, connection refused).
111
115
  */
112
- path: string;
116
+ ping(): Promise<boolean>;
117
+ /**
118
+ * Get full ping response from the API server
119
+ * @returns Promise resolving to the full PingResponse object.
120
+ */
121
+ getPingResponse(): Promise<PingResponse>;
122
+ /**
123
+ * Fetches platform configuration from the API.
124
+ * @returns Promise resolving to the config response.
125
+ * @throws {ShipError} If the config request fails.
126
+ */
127
+ getConfig(): Promise<ConfigResponse>;
128
+ /**
129
+ * Deploys an array of StaticFile objects to the Ship API.
130
+ * Constructs and sends a multipart/form-data POST request, handling both browser and Node.js environments.
131
+ * Validates files and manages deploy progress and error translation.
132
+ * @param files - Array of StaticFile objects to deploy (must include MD5 checksums).
133
+ * @param options - Optional per-deploy configuration (overrides instance defaults).
134
+ * @returns Promise resolving to a full Deployment object on success.
135
+ * @throws {ShipFileError} If a file is missing an MD5 checksum or content type is unsupported.
136
+ * @throws {ShipClientError} If no files are provided or if environment is unknown.
137
+ * @throws {ShipNetworkError} If a network error occurs during deploy.
138
+ * @throws {ShipApiError} If the API returns an error response.
139
+ * @throws {ShipCancelledError} If the deploy is cancelled via an AbortSignal.
140
+ */
141
+ deploy(files: StaticFile[], options?: ApiDeployOptions): Promise<Deployment>;
142
+ /**
143
+ * Lists all deployments for the authenticated account
144
+ * @returns Promise resolving to deployment list response
145
+ */
146
+ listDeployments(): Promise<DeploymentListResponse>;
147
+ /**
148
+ * Gets a specific deployment by ID
149
+ * @param id - Deployment ID to retrieve
150
+ * @returns Promise resolving to deployment details
151
+ */
152
+ getDeployment(id: string): Promise<Deployment>;
153
+ /**
154
+ * Removes a deployment by ID
155
+ * @param id - Deployment ID to remove
156
+ * @returns Promise resolving when removal is complete
157
+ */
158
+ removeDeployment(id: string): Promise<void>;
159
+ /**
160
+ * Sets an alias (create or update)
161
+ * @param name - Alias name
162
+ * @param deployment - Deployment name to point to
163
+ * @returns Promise resolving to the created/updated alias with operation context
164
+ */
165
+ setAlias(name: string, deployment: string): Promise<_shipstatic_types.Alias>;
166
+ /**
167
+ * Gets a specific alias by name
168
+ * @param name - Alias name to retrieve
169
+ * @returns Promise resolving to alias details
170
+ */
171
+ getAlias(name: string): Promise<Alias>;
172
+ /**
173
+ * Lists all aliases for the authenticated account
174
+ * @returns Promise resolving to alias list response
175
+ */
176
+ listAliases(): Promise<AliasListResponse>;
177
+ /**
178
+ * Removes an alias by name
179
+ * @param name - Alias name to remove
180
+ * @returns Promise resolving to removal confirmation
181
+ */
182
+ removeAlias(name: string): Promise<void>;
183
+ /**
184
+ * Triggers a manual DNS check for an external alias
185
+ * @param name - Alias name to check DNS for
186
+ * @returns Promise resolving to confirmation message
187
+ */
188
+ checkAlias(name: string): Promise<{
189
+ message: string;
190
+ }>;
191
+ /**
192
+ * Gets account details for the authenticated user
193
+ * @returns Promise resolving to account details
194
+ */
195
+ getAccount(): Promise<Account>;
113
196
  /**
114
- * The original absolute file system path (primarily used in Node.js environments).
115
- * This helps in debugging or associating the server path back to its source.
197
+ * Creates a new API key for the authenticated user
198
+ * @returns Promise resolving to the new API key
116
199
  */
117
- filePath?: string;
200
+ createApiKey(): Promise<{
201
+ apiKey: string;
202
+ }>;
118
203
  /**
119
- * The MD5 hash (checksum) of the file's content.
120
- * This is calculated by the SDK before deploy if not provided.
204
+ * Checks if files represent a SPA structure using AI analysis
205
+ * @param files - Array of StaticFile objects to analyze
206
+ * @returns Promise resolving to boolean indicating if it's a SPA
121
207
  */
122
- md5?: string;
123
- /** The size of the file in bytes. */
124
- size: number;
208
+ checkSPA(files: StaticFile[]): Promise<boolean>;
125
209
  }
126
210
 
127
211
  /**
128
212
  * @file All Ship SDK resources in one place - impossibly simple.
129
213
  */
130
214
 
131
- interface DeploymentResource {
132
- create: (input: DeployInput, options?: DeploymentOptions) => Promise<Deployment>;
133
- list: () => Promise<DeploymentListResponse>;
134
- remove: (id: string) => Promise<void>;
135
- get: (id: string) => Promise<Deployment>;
136
- }
137
- interface AliasResource {
138
- set: (aliasName: string, deployment: string) => Promise<Alias>;
139
- get: (aliasName: string) => Promise<Alias>;
140
- list: () => Promise<AliasListResponse>;
141
- remove: (aliasName: string) => Promise<void>;
142
- check: (aliasName: string) => Promise<{
143
- message: string;
144
- }>;
145
- }
146
- interface AccountResource {
147
- get: () => Promise<Account>;
215
+ declare function createDeploymentResource(getApi: () => ApiHttp, clientDefaults?: ShipClientOptions, ensureInit?: () => Promise<void>, processInput?: (input: DeployInput, options: DeploymentOptions) => Promise<StaticFile[]>): DeploymentResource;
216
+ declare function createAliasResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): AliasResource;
217
+ declare function createAccountResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): AccountResource;
218
+ declare function createKeysResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): KeysResource;
219
+
220
+ /**
221
+ * Abstract base class for Ship SDK implementations.
222
+ *
223
+ * Provides shared functionality while allowing environment-specific
224
+ * implementations to handle configuration loading and deployment processing.
225
+ */
226
+ declare abstract class Ship$1 {
227
+ protected http: ApiHttp;
228
+ protected readonly clientOptions: ShipClientOptions;
229
+ protected initPromise: Promise<void> | null;
230
+ protected _deployments: DeploymentResource;
231
+ protected _aliases: AliasResource;
232
+ protected _account: AccountResource;
233
+ protected _keys: KeysResource;
234
+ constructor(options?: ShipClientOptions);
235
+ protected abstract resolveInitialConfig(options: ShipClientOptions): any;
236
+ protected abstract loadFullConfig(): Promise<void>;
237
+ protected abstract processInput(input: DeployInput, options: DeploymentOptions): Promise<StaticFile[]>;
238
+ /**
239
+ * Ensure full initialization is complete - called lazily by resources
240
+ */
241
+ protected ensureInitialized(): Promise<void>;
242
+ /**
243
+ * Ping the API server to check connectivity
244
+ */
245
+ ping(): Promise<boolean>;
246
+ /**
247
+ * Deploy project (convenience shortcut to ship.deployments.create())
248
+ */
249
+ deploy(input: DeployInput, options?: DeploymentOptions): Promise<Deployment>;
250
+ /**
251
+ * Get current account information (convenience shortcut to ship.account.get())
252
+ */
253
+ whoami(): Promise<_shipstatic_types.Account>;
254
+ /**
255
+ * Get deployments resource (environment-specific)
256
+ */
257
+ get deployments(): DeploymentResource;
258
+ /**
259
+ * Get aliases resource
260
+ */
261
+ get aliases(): AliasResource;
262
+ /**
263
+ * Get account resource
264
+ */
265
+ get account(): AccountResource;
266
+ /**
267
+ * Get keys resource
268
+ */
269
+ get keys(): KeysResource;
148
270
  }
149
- interface KeysResource {
150
- create: () => Promise<{
151
- apiKey: string;
152
- }>;
271
+
272
+ /**
273
+ * @file Shared configuration logic for both environments.
274
+ */
275
+
276
+ type Config = PlatformConfig;
277
+ /**
278
+ * Universal configuration resolver for all environments.
279
+ * This is the single source of truth for config resolution.
280
+ */
281
+ declare function resolveConfig(userOptions?: ShipClientOptions, loadedConfig?: Partial<ShipClientOptions>): {
282
+ apiUrl: string;
283
+ apiKey?: string;
284
+ deployToken?: string;
285
+ };
286
+ /**
287
+ * Merge deployment options with client defaults.
288
+ * This is shared logic used by both environments.
289
+ */
290
+ declare function mergeDeployOptions(options: DeploymentOptions, clientDefaults: ShipClientOptions): DeploymentOptions;
291
+
292
+ interface MD5Result {
293
+ md5: string;
153
294
  }
295
+ /**
296
+ * Unified MD5 calculation that delegates to environment-specific handlers
297
+ */
298
+ declare function calculateMD5(input: Blob | Buffer | string): Promise<MD5Result>;
154
299
 
155
300
  /**
156
- * Processes Node.js file and directory paths into an array of StaticFile objects ready for deploy.
157
- * Now uses the simplified, declarative approach suggested in the feedback.
301
+ * Utility functions for string manipulation.
302
+ */
303
+ /**
304
+ * Simple utility to pluralize a word based on a count.
305
+ * @param count The number to determine pluralization.
306
+ * @param singular The singular form of the word.
307
+ * @param plural The plural form of the word.
308
+ * @param includeCount Whether to include the count in the returned string. Defaults to true.
309
+ * @returns A string with the count and the correctly pluralized word.
310
+ */
311
+ declare function pluralize(count: number, singular: string, plural: string, includeCount?: boolean): string;
312
+
313
+ /**
314
+ * List of directory names considered as junk
158
315
  *
159
- * @param paths - File or directory paths to scan and process.
160
- * @param options - Processing options (basePath, preserveDirs).
161
- * @returns Promise resolving to an array of StaticFile objects.
162
- * @throws {ShipClientError} If called outside Node.js or if fs/path modules fail.
316
+ * Files within these directories (at any level in the path hierarchy) will be excluded.
317
+ * The comparison is case-insensitive for cross-platform compatibility.
318
+ *
319
+ * @internal
163
320
  */
164
- declare function processFilesForNode(paths: string[], options?: DeploymentOptions): Promise<StaticFile[]>;
321
+ declare const JUNK_DIRECTORIES: readonly ["__MACOSX", ".Trashes", ".fseventsd", ".Spotlight-V100"];
322
+ /**
323
+ * Filters an array of file paths, removing those considered junk
324
+ *
325
+ * A path is filtered out if either:
326
+ * 1. The basename is identified as junk by the 'junk' package (e.g., .DS_Store, Thumbs.db)
327
+ * 2. Any directory segment in the path matches an entry in JUNK_DIRECTORIES (case-insensitive)
328
+ *
329
+ * All path separators are normalized to forward slashes for consistent cross-platform behavior.
330
+ *
331
+ * @param filePaths - An array of file path strings to filter
332
+ * @returns A new array containing only non-junk file paths
333
+ */
334
+ declare function filterJunk(filePaths: string[]): string[];
165
335
 
166
336
  /**
167
- * Processes browser files (FileList or File[]) into an array of StaticFile objects ready for deploy.
168
- * Calculates MD5, filters junk files, and applies automatic path optimization.
337
+ * @file Deploy path optimization - the core logic that makes Ship deployments clean and intuitive.
338
+ * Automatically strips common parent directories to create clean deployment URLs.
339
+ */
340
+ /**
341
+ * Represents a file ready for deployment with its optimized path
342
+ */
343
+ interface DeployFile {
344
+ /** The clean deployment path (e.g., "assets/style.css") */
345
+ path: string;
346
+ /** Original filename */
347
+ name: string;
348
+ }
349
+ /**
350
+ * Core path optimization logic.
351
+ * Transforms messy local paths into clean deployment paths.
169
352
  *
170
- * @param browserFiles - FileList or File[] to process for deploy.
171
- * @param options - Processing options including pathDetect for automatic path optimization.
172
- * @returns Promise resolving to an array of StaticFile objects.
173
- * @throws {ShipClientError} If called outside a browser or with invalid input.
353
+ * @example
354
+ * Input: ["dist/index.html", "dist/assets/app.js"]
355
+ * Output: ["index.html", "assets/app.js"]
356
+ *
357
+ * @param filePaths - Raw file paths from the local filesystem
358
+ * @param options - Path processing options
174
359
  */
175
- declare function processFilesForBrowser(browserFiles: FileList | File[], options?: DeploymentOptions): Promise<StaticFile[]>;
360
+ declare function optimizeDeployPaths(filePaths: string[], options?: {
361
+ flatten?: boolean;
362
+ }): DeployFile[];
176
363
 
177
364
  /**
178
365
  * @file Environment detection utilities for the Ship SDK.
@@ -193,73 +380,89 @@ type ExecutionEnvironment = 'browser' | 'node' | 'unknown';
193
380
  * @internal
194
381
  */
195
382
  declare function __setTestEnvironment(env: ExecutionEnvironment | null): void;
383
+ /**
384
+ * Gets the current effective execution environment.
385
+ *
386
+ * This function first checks if a test environment override is active via {@link __setTestEnvironment}.
387
+ * If not, it detects the actual environment (Node.js, browser, or unknown).
388
+ *
389
+ * @returns The current execution environment: 'browser', 'node', or 'unknown'.
390
+ * @public
391
+ */
392
+ declare function getENV(): ExecutionEnvironment;
393
+
394
+ /**
395
+ * @file Manages loading and validation of client configuration.
396
+ * This module uses `cosmiconfig` to find and load configuration from various
397
+ * file sources (e.g., `.shiprc`, `package.json`) and environment variables.
398
+ * Configuration values are validated using Zod schemas.
399
+ */
196
400
 
197
401
  /**
198
- * Ship SDK Client - Universal class-based interface for both Node.js and browser environments.
402
+ * Simplified configuration loading prioritizing environment variables.
403
+ * Only loads file config if environment variables are not set.
404
+ * Only available in Node.js environments.
199
405
  *
200
- * Authentication Options:
201
- * ```
406
+ * @param configFile - Optional specific config file path to load
407
+ * @returns Configuration object with loaded values
408
+ * @throws {ShipInvalidConfigError} If the configuration is invalid.
409
+ */
410
+ declare function loadConfig(configFile?: string): Promise<Partial<ShipClientOptions>>;
411
+
412
+ /**
413
+ * @file Platform configuration management for the Ship SDK.
414
+ * Implements fail-fast dynamic configuration with mandatory API fetch.
415
+ */
416
+
417
+ /**
418
+ * Set the current config (called after fetching from API)
419
+ */
420
+ declare function setConfig(config: ConfigResponse): void;
421
+ /**
422
+ * Get current config - throws if not initialized (fail-fast approach)
423
+ * @throws {ShipError.config} If configuration hasn't been fetched from API
424
+ */
425
+ declare function getCurrentConfig(): ConfigResponse;
426
+
427
+ /**
428
+ * Processes Node.js file and directory paths into an array of StaticFile objects ready for deploy.
429
+ * Uses corrected logic to properly handle common parent directory stripping.
430
+ *
431
+ * @param paths - File or directory paths to scan and process.
432
+ * @param options - Processing options (pathDetect, etc.).
433
+ * @returns Promise resolving to an array of StaticFile objects.
434
+ * @throws {ShipClientError} If called outside Node.js or if fs/path modules fail.
435
+ */
436
+ declare function processFilesForNode(paths: string[], options?: DeploymentOptions): Promise<StaticFile[]>;
437
+
438
+ /**
439
+ * @file Ship SDK for Node.js environments with full file system support.
440
+ */
441
+
442
+ /**
443
+ * Ship SDK Client for Node.js environments.
444
+ *
445
+ * Provides full file system access, configuration file loading,
446
+ * and environment variable support.
447
+ *
448
+ * @example
449
+ * ```typescript
202
450
  * // Authenticated deployments with API key
203
451
  * const ship = new Ship({ apiKey: "ship-xxxx" });
204
452
  *
205
453
  * // Single-use deployments with deploy token
206
454
  * const ship = new Ship({ deployToken: "token-xxxx" });
207
- * ```
208
455
  *
209
- * Automatically detects the environment and handles Node.js and browser deploys directly.
210
- * In Node.js environments, loads configuration from files and environment variables.
211
- * In browser environments, uses only the provided options.
456
+ * // Deploy a directory
457
+ * await ship.deploy('./dist');
458
+ * ```
212
459
  */
213
- declare class Ship {
214
- private http;
215
- private environment;
216
- private readonly clientOptions;
217
- private initPromise;
218
- private _deployments;
219
- private _aliases;
220
- private _account;
221
- private _keys;
460
+ declare class Ship extends Ship$1 {
461
+ #private;
222
462
  constructor(options?: ShipClientOptions);
223
- /**
224
- * Ensure full initialization is complete - called lazily by resources
225
- */
226
- private ensureInitialized;
227
- /**
228
- * Helper method to create initialization callback for resources
229
- */
230
- private getInitCallback;
231
- /**
232
- * Initialize config from file/env and platform config from API
233
- */
234
- private initializeConfig;
235
- /**
236
- * Ping the API server to check connectivity
237
- */
238
- ping(): Promise<boolean>;
239
- /**
240
- * Deploy project (convenience shortcut to ship.deployments.create())
241
- */
242
- deploy(input: DeployInput, options?: DeploymentOptions): Promise<Deployment>;
243
- /**
244
- * Get current account information (convenience shortcut to ship.account.get())
245
- */
246
- whoami(): Promise<_shipstatic_types.Account>;
247
- /**
248
- * Get deployments resource (environment-specific)
249
- */
250
- get deployments(): DeploymentResource;
251
- /**
252
- * Get aliases resource
253
- */
254
- get aliases(): AliasResource;
255
- /**
256
- * Get account resource
257
- */
258
- get account(): AccountResource;
259
- /**
260
- * Get keys resource
261
- */
262
- get keys(): KeysResource;
463
+ protected resolveInitialConfig(options: ShipClientOptions): any;
464
+ protected loadFullConfig(): Promise<void>;
465
+ protected processInput(input: DeployInput, options: DeploymentOptions): Promise<StaticFile[]>;
263
466
  }
264
467
 
265
- export { type AccountResource, type AliasResource, type ApiDeployOptions, type DeployInput, type DeploymentOptions, type DeploymentResource, type KeysResource, type ProgressStats, Ship, type ShipClientOptions, type StaticFile, __setTestEnvironment, Ship as default, processFilesForBrowser, processFilesForNode };
468
+ export { type ApiDeployOptions, ApiHttp, type Config, type DeployFile, type DeploymentOptions, type ExecutionEnvironment, JUNK_DIRECTORIES, type MD5Result, type ProgressStats, Ship, type ShipClientOptions, __setTestEnvironment, calculateMD5, createAccountResource, createAliasResource, createDeploymentResource, createKeysResource, Ship as default, filterJunk, getCurrentConfig, getENV, loadConfig, mergeDeployOptions, optimizeDeployPaths, pluralize, processFilesForNode, resolveConfig, setConfig };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var me=Object.defineProperty;var fe=Object.getOwnPropertyDescriptor;var ue=Object.getOwnPropertyNames;var he=Object.prototype.hasOwnProperty;var G=(i,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of ue(e))!he.call(i,n)&&n!==t&&me(i,n,{get:()=>e[n],enumerable:!(o=fe(e,n))||o.enumerable});return i},J=(i,e,t)=>(G(i,e,"default"),t&&G(t,e,"default"));var U=null;function ye(i){U=i}function de(){return typeof process<"u"&&process.versions&&process.versions.node?"node":typeof window<"u"||typeof self<"u"?"browser":"unknown"}function f(){return U||de()}import{z as C}from"zod";var D={};J(D,Ue);import*as Ue from"@shipstatic/types";var k="https://api.shipstatic.com";var z="ship",ge=C.object({apiUrl:C.string().url().optional(),apiKey:C.string().optional(),deployToken:C.string().optional()}).strict();function V(i){try{return ge.parse(i)}catch(e){if(e instanceof C.ZodError){let t=e.issues[0],o=t.path.length>0?` at ${t.path.join(".")}`:"";throw D.ShipError.config(`Configuration validation failed${o}: ${t.message}`)}throw D.ShipError.config("Configuration validation failed")}}async function we(i){try{if(f()!=="node")return{};let{cosmiconfigSync:e}=await import("cosmiconfig"),t=await import("os"),o=e(z,{searchPlaces:[`.${z}rc`,"package.json",`${t.homedir()}/.${z}rc`],stopDir:t.homedir()}),n;if(i?n=o.load(i):n=o.search(),n&&n.config)return V(n.config)}catch(e){if(e instanceof D.ShipError)throw e}return{}}async function W(i){if(f()!=="node")return{};let e={apiUrl:process.env.SHIP_API_URL,apiKey:process.env.SHIP_API_KEY,deployToken:process.env.SHIP_DEPLOY_TOKEN},t=await we(i),o={apiUrl:e.apiUrl??t.apiUrl,apiKey:e.apiKey??t.apiKey,deployToken:e.deployToken??t.deployToken};return V(o)}function L(i={},e={}){let t={apiUrl:i.apiUrl||e.apiUrl||k,apiKey:i.apiKey||e.apiKey,deployToken:i.deployToken||e.deployToken},o={apiUrl:t.apiUrl};return t.apiKey!==void 0&&(o.apiKey=t.apiKey),t.deployToken!==void 0&&(o.deployToken=t.deployToken),o}function Y(i={},e){return{onProgress:e.onProgress,onProgressStats:e.onProgressStats,maxConcurrency:e.maxConcurrentDeploys,timeout:e.timeout,apiKey:e.apiKey,deployToken:e.deployToken,apiUrl:e.apiUrl,...i}}import*as O from"mime-types";import{ShipError as y}from"@shipstatic/types";var T="/deployments",X="/ping",x="/aliases",Pe="/config",Se="/account",De="/spa-check";function Fe(i){return typeof i=="string"?O.lookup(i)||"application/octet-stream":O.lookup(i.name)||i.type||"application/octet-stream"}async function Ae(i){let e=[];for await(let t of i)e.push(t);return e}var E=class{constructor(e){this.apiUrl=e.apiUrl||k,this.apiKey=e.apiKey??"",this.deployToken=e.deployToken??""}#n(e={}){let t={...e};return this.deployToken?t.Authorization=`Bearer ${this.deployToken}`:this.apiKey&&(t.Authorization=`Bearer ${this.apiKey}`),t}async#i(e,t={},o){let n=this.#n(t.headers),s={...t,headers:n,credentials:f()==="browser"?"include":"same-origin"};try{return await fetch(e,s)}catch(r){throw this.#t(r,o),r}}async#e(e,t={},o){try{let n=await this.#i(e,t,o);return n.ok||await this.#o(n,o),n.headers.get("Content-Length")==="0"||n.status===204?void 0:await n.json()}catch(n){throw n instanceof y||this.#t(n,o),n}}async ping(){return(await this.#e(`${this.apiUrl}${X}`,{method:"GET"},"Ping"))?.success||!1}async getPingResponse(){return await this.#e(`${this.apiUrl}${X}`,{method:"GET"},"Ping")}async getConfig(){return await this.#e(`${this.apiUrl}${Pe}`,{method:"GET"},"Config")}async deploy(e,t={}){this.#s(e);let{apiUrl:o=this.apiUrl,signal:n,apiKey:s,deployToken:r}=t,{requestBody:c,requestHeaders:m}=await this.#r(e),u={};r?u={Authorization:`Bearer ${r}`}:s&&(u={Authorization:`Bearer ${s}`});let a={method:"POST",body:c,headers:{...m,...u},signal:n||null};return await this.#e(`${o}${T}`,a,"Deploy")}#s(e){if(!e.length)throw y.business("No files to deploy.");for(let t of e)if(!t.md5)throw y.file(`MD5 checksum missing for file: ${t.path}`,t.path)}async#r(e){let t,o={};if(f()==="browser")t=this.#a(e);else if(f()==="node"){let{body:n,headers:s}=await this.#l(e);t=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength),o=s}else throw y.business("Unknown or unsupported execution environment");return{requestBody:t,requestHeaders:o}}#a(e){let t=new FormData,o=[];for(let n=0;n<e.length;n++){let s=e[n],r;if(s.content instanceof File||s.content instanceof Blob)r=s.content;else throw y.file(`Unsupported file.content type for browser FormData: ${s.path}`,s.path);let c=Fe(r instanceof File?r:s.path),m=new File([r],s.path,{type:c});t.append("files[]",m),o.push(s.md5)}return t.append("checksums",JSON.stringify(o)),t}async#l(e){let{FormData:t,File:o}=await import("formdata-node"),{FormDataEncoder:n}=await import("form-data-encoder"),s=await import("path"),r=new t,c=[];for(let l=0;l<e.length;l++){let p=e[l],P=O.lookup(p.path)||"application/octet-stream",d;if(Buffer.isBuffer(p.content))d=new o([p.content],p.path,{type:P});else if(typeof Blob<"u"&&p.content instanceof Blob)d=new o([p.content],p.path,{type:P});else throw y.file(`Unsupported file.content type for Node.js FormData: ${p.path}`,p.path);let F=p.path.startsWith("/")?p.path:"/"+p.path;r.append("files[]",d,F),c.push(p.md5)}r.append("checksums",JSON.stringify(c));let m=new n(r),u=await Ae(m.encode()),a=Buffer.concat(u.map(l=>Buffer.from(l))),h={"Content-Type":m.contentType,"Content-Length":Buffer.byteLength(a).toString()};return{body:a,headers:h}}async#o(e,t){let o={};try{let n=e.headers.get("content-type");n&&n.includes("application/json")?o=await e.json():o={message:await e.text()}}catch{o={message:"Failed to parse error response"}}if(o.error||o.code||o.message){let n=o.message||o.error||`${t} failed due to API error`;throw e.status===401?y.authentication(n):y.api(n,e.status,o.code,o)}throw e.status===401?y.authentication(`Authentication failed for ${t}`):y.api(`${t} failed due to API error`,e.status,void 0,o)}#t(e,t){throw e.name==="AbortError"?y.cancelled(`${t} operation was cancelled.`):e instanceof TypeError&&e.message.includes("fetch")?y.network(`${t} failed due to network error: ${e.message}`,e):e instanceof y?e:y.business(`An unexpected error occurred during ${t}: ${e.message||"Unknown error"}`)}async listDeployments(){return await this.#e(`${this.apiUrl}${T}`,{method:"GET"},"List Deployments")}async getDeployment(e){return await this.#e(`${this.apiUrl}${T}/${e}`,{method:"GET"},"Get Deployment")}async removeDeployment(e){await this.#e(`${this.apiUrl}${T}/${e}`,{method:"DELETE"},"Remove Deployment")}async setAlias(e,t){try{let o=await this.#i(`${this.apiUrl}${x}/${encodeURIComponent(e)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({deployment:t})},"Set Alias");return o.ok||await this.#o(o,"Set Alias"),{...await o.json(),isCreate:o.status===201}}catch(o){this.#t(o,"Set Alias")}}async getAlias(e){return await this.#e(`${this.apiUrl}${x}/${encodeURIComponent(e)}`,{method:"GET"},"Get Alias")}async listAliases(){return await this.#e(`${this.apiUrl}${x}`,{method:"GET"},"List Aliases")}async removeAlias(e){await this.#e(`${this.apiUrl}${x}/${encodeURIComponent(e)}`,{method:"DELETE"},"Remove Alias")}async checkAlias(e){return await this.#e(`${this.apiUrl}${x}/${encodeURIComponent(e)}/dns-check`,{method:"POST"},"Check Alias")}async getAccount(){return await this.#e(`${this.apiUrl}${Se}`,{method:"GET"},"Get Account")}async createApiKey(){return await this.#e(`${this.apiUrl}/key`,{method:"POST"},"Create API Key")}async checkSPA(e){let t=e.find(m=>m.path==="index.html"||m.path==="/index.html");if(!t)return!1;let o=100*1024;if(t.size>o)return!1;let n;if(Buffer.isBuffer(t.content))n=t.content.toString("utf-8");else if(typeof Blob<"u"&&t.content instanceof Blob)n=await t.content.text();else if(typeof File<"u"&&t.content instanceof File)n=await t.content.text();else return!1;let r={files:e.map(m=>m.path),index:n};return(await this.#e(`${this.apiUrl}${De}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)},"SPA Check")).isSPA}};import{ShipError as Ie}from"@shipstatic/types";import{ShipError as be}from"@shipstatic/types";var _=null;function Z(i){_=i}function R(){if(_===null)throw be.config("Platform configuration not initialized. The SDK must fetch configuration from the API before performing operations.");return _}import{ShipError as w,DEPLOYMENT_CONFIG_FILENAME as H}from"@shipstatic/types";import{ShipError as A}from"@shipstatic/types";async function ve(i){let e=(await import("spark-md5")).default;return new Promise((t,o)=>{let s=Math.ceil(i.size/2097152),r=0,c=new e.ArrayBuffer,m=new FileReader,u=()=>{let a=r*2097152,h=Math.min(a+2097152,i.size);m.readAsArrayBuffer(i.slice(a,h))};m.onload=a=>{let h=a.target?.result;if(!h){o(A.business("Failed to read file chunk"));return}c.append(h),r++,r<s?u():t({md5:c.end()})},m.onerror=()=>{o(A.business("Failed to calculate MD5: FileReader error"))},u()})}async function Ce(i){let e=await import("crypto");if(Buffer.isBuffer(i)){let o=e.createHash("md5");return o.update(i),{md5:o.digest("hex")}}let t=await import("fs");return new Promise((o,n)=>{let s=e.createHash("md5"),r=t.createReadStream(i);r.on("error",c=>n(A.business(`Failed to read file for MD5: ${c.message}`))),r.on("data",c=>s.update(c)),r.on("end",()=>o({md5:s.digest("hex")}))})}async function b(i){let e=f();if(e==="browser"){if(!(i instanceof Blob))throw A.business("Invalid input for browser MD5 calculation: Expected Blob or File.");return ve(i)}if(e==="node"){if(!(Buffer.isBuffer(i)||typeof i=="string"))throw A.business("Invalid input for Node.js MD5 calculation: Expected Buffer or file path string.");return Ce(i)}throw A.business("Unknown or unsupported execution environment for MD5 calculation.")}import{isJunk as xe}from"junk";var Ee=["__MACOSX",".Trashes",".fseventsd",".Spotlight-V100"];function $(i){return!i||i.length===0?[]:i.filter(e=>{if(!e)return!1;let t=e.replace(/\\/g,"/").split("/").filter(Boolean);if(t.length===0)return!0;let o=t[t.length-1];if(xe(o))return!1;let n=t.slice(0,-1);for(let s of n)if(Ee.some(r=>s.toLowerCase()===r.toLowerCase()))return!1;return!0})}import{ShipError as v}from"@shipstatic/types";function I(i){return i.replace(/\\/g,"/").replace(/\/+/g,"/").replace(/^\/+/,"")}function B(i,e={}){if(e.flatten===!1)return i.map(o=>({path:I(o),name:M(o)}));let t=Re(i);return i.map(o=>{let n=I(o);if(t){let s=t.endsWith("/")?t:`${t}/`;n.startsWith(s)&&(n=n.substring(s.length))}return n||(n=M(o)),{path:n,name:M(o)}})}function Re(i){if(!i.length)return"";let t=i.map(s=>I(s)).map(s=>s.split("/")),o=[],n=Math.min(...t.map(s=>s.length));for(let s=0;s<n-1;s++){let r=t[0][s];if(t.every(c=>c[s]===r))o.push(r);else break}return o.join("/")}function M(i){return i.split(/[/\\]/).pop()||i}import*as S from"fs";import*as g from"path";function ee(i){let e=[];try{let t=S.readdirSync(i);for(let o of t){let n=g.join(i,o),s=S.statSync(n);if(s.isDirectory()){let r=ee(n);e.push(...r)}else s.isFile()&&e.push(n)}}catch(t){console.error(`Error reading directory ${i}:`,t)}return e}async function Q(i,e={}){let t=g.resolve(i),n=(()=>{let l=S.statSync(t);return l.isFile()?[t]:l.isDirectory()?ee(t):[]})().filter(l=>{let p=g.basename(l);return $([p]).length>0}),r=S.statSync(t).isDirectory()?t:g.dirname(t),c=n.map(l=>g.relative(r,l).replace(/\\/g,"/")||g.basename(l)),m=B(c,{flatten:e.pathDetect!==!1}),u=[],a=0;for(let l=0;l<n.length;l++){let p=n[l],P=m[l].path;try{let d=S.statSync(p);if(d.size===0){console.warn(`Skipping empty file: ${p}`);continue}let F=R();if(d.size>F.maxFileSize)throw v.business(`File ${p} is too large. Maximum allowed size is ${F.maxFileSize/(1024*1024)}MB.`);if(a+=d.size,a>F.maxTotalSize)throw v.business(`Total deploy size is too large. Maximum allowed is ${F.maxTotalSize/(1024*1024)}MB.`);let N=S.readFileSync(p),{md5:ce}=await b(N);if(P.includes("\0")||P.includes("/../")||P.startsWith("../")||P.endsWith("/.."))throw v.business(`Security error: Unsafe file path "${P}" for file: ${p}`);u.push({path:P,content:N,size:N.length,md5:ce})}catch(d){if(d instanceof v&&d.isClientError&&d.isClientError())throw d;console.error(`Could not process file ${p}:`,d)}}let h=R();if(u.length>h.maxFilesCount)throw v.business(`Too many files to deploy. Maximum allowed is ${h.maxFilesCount} files.`);return u}async function K(i,e={}){if(f()!=="node")throw v.business("processFilesForNode can only be called in a Node.js environment.");if(i.length>1){let t=[];for(let o of i){let n=await Q(o,e);t.push(...n)}return t}return await Q(i[0],e)}import{ShipError as te}from"@shipstatic/types";async function j(i,e={}){if(f()!=="browser")throw te.business("processFilesForBrowser can only be called in a browser environment.");let t=Array.isArray(i)?i:Array.from(i),o=t.map(a=>a.webkitRelativePath||a.name),n=B(o,{flatten:e.pathDetect!==!1}),s=[];for(let a=0;a<t.length;a++){let h=t[a],l=n[a].path;if(l.includes("..")||l.includes("\0"))throw te.business(`Security error: Unsafe file path "${l}" for file: ${h.name}`);s.push({file:h,relativePath:l})}let r=s.map(a=>a.relativePath),c=$(r),m=new Set(c),u=[];for(let a of s){if(!m.has(a.relativePath)||a.file.size===0)continue;let{md5:h}=await b(a.file);u.push({content:a.file,path:a.relativePath,size:a.file.size,md5:h})}return u}function ie(i,e={}){let t=R();if(!e.skipEmptyCheck&&i.length===0)throw w.business("No files to deploy.");if(i.length>t.maxFilesCount)throw w.business(`Too many files to deploy. Maximum allowed is ${t.maxFilesCount}.`);let o=0;for(let n of i){if(n.size>t.maxFileSize)throw w.business(`File ${n.name} is too large. Maximum allowed size is ${t.maxFileSize/(1024*1024)}MB.`);if(o+=n.size,o>t.maxTotalSize)throw w.business(`Total deploy size is too large. Maximum allowed is ${t.maxTotalSize/(1024*1024)}MB.`)}}function oe(i,e){if(e==="node"){if(!Array.isArray(i))throw w.business("Invalid input type for Node.js environment. Expected string[] file paths.");if(i.length===0)throw w.business("No files to deploy.");if(!i.every(t=>typeof t=="string"))throw w.business("Invalid input type for Node.js environment. Expected string[] file paths.")}else if(e==="browser"&&i instanceof HTMLInputElement&&!i.files)throw w.business("No files selected in HTMLInputElement")}function ne(i){return ie(i,{skipEmptyCheck:!0}),i.forEach(e=>{e.path&&(e.path=e.path.replace(/\\/g,"/"))}),i}async function ke(i,e={}){oe(i,"node");let t=await K(i,e);return ne(t)}async function Te(i,e={}){oe(i,"browser");let t;if(i instanceof HTMLInputElement)t=Array.from(i.files);else if(typeof i=="object"&&i!==null&&typeof i.length=="number"&&typeof i.item=="function")t=Array.from(i);else if(Array.isArray(i)){if(i.length>0&&typeof i[0]=="string")throw w.business("Invalid input type for browser environment. Expected File[], FileList, or HTMLInputElement.");t=i}else throw w.business("Invalid input type for browser environment. Expected File[], FileList, or HTMLInputElement.");t=t.filter(n=>n.size===0?(console.warn(`Skipping empty file: ${n.name}`),!1):!0),ie(t);let o=await j(t,e);return ne(o)}async function se(i,e={},t){let o=f();if(o!=="node"&&o!=="browser")throw w.business("Unsupported execution environment.");let n;return o==="node"?n=await ke(i,e):n=await Te(i,e),t&&(n=await $e(n,t,e)),n}async function Oe(){let i={rewrites:[{source:"/(.*)",destination:"/index.html"}]},e=Buffer.from(JSON.stringify(i,null,2),"utf-8"),{md5:t}=await b(e);return{path:H,content:e,size:e.length,md5:t}}async function $e(i,e,t){if(t.spaDetect===!1||i.some(o=>o.path===H))return i;try{if(await e.checkSPA(i)){let n=await Oe();return console.log(`SPA detected - generated ${H}`),[...i,n]}}catch{console.warn("SPA detection failed, continuing without auto-config")}return i}function re(i,e,t){return{create:async(o,n={})=>{t&&await t();let s=e?Y(n,e):n,r=i(),c=await se(o,s,r);return await r.deploy(c,s)},list:async()=>(t&&await t(),i().listDeployments()),remove:async o=>{t&&await t(),await i().removeDeployment(o)},get:async o=>(t&&await t(),i().getDeployment(o))}}function ae(i,e){return{set:async(t,o)=>(e&&await e(),i().setAlias(t,o)),get:async t=>(e&&await e(),i().getAlias(t)),list:async()=>(e&&await e(),i().listAliases()),remove:async t=>{e&&await e(),await i().removeAlias(t)},check:async t=>(e&&await e(),i().checkAlias(t))}}function le(i,e){return{get:async()=>(e&&await e(),i().getAccount())}}function pe(i,e){return{create:async()=>(e&&await e(),i().createApiKey())}}import{ShipError as zt,ShipErrorType as Lt}from"@shipstatic/types";var q=class{constructor(e={}){this.initPromise=null;if(this.clientOptions=e,this.environment=f(),this.environment!=="node"&&this.environment!=="browser")throw Ie.business("Unsupported execution environment.");let t=L(e,{});this.http=new E({...e,...t});let o=this.getInitCallback(),n=()=>this.http;this._deployments=re(n,this.clientOptions,o),this._aliases=ae(n,o),this._account=le(n,o),this._keys=pe(n,o)}async ensureInitialized(){return this.initPromise||(this.initPromise=this.initializeConfig()),this.initPromise}getInitCallback(){return()=>this.ensureInitialized()}async initializeConfig(){try{let e=await W(this.clientOptions.configFile),t=L(this.clientOptions,e);this.http=new E({...this.clientOptions,...t});let o=await this.http.getConfig();Z(o)}catch(e){throw this.initPromise=null,e}}async ping(){return await this.ensureInitialized(),this.http.ping()}async deploy(e,t){return this.deployments.create(e,t)}async whoami(){return this.account.get()}get deployments(){return this._deployments}get aliases(){return this._aliases}get account(){return this._account}get keys(){return this._keys}},Bt=q;export{q as Ship,zt as ShipError,Lt as ShipErrorType,ye as __setTestEnvironment,Bt as default,j as processFilesForBrowser,K as processFilesForNode};
1
+ var Fe=Object.defineProperty;var _e=Object.getOwnPropertyDescriptor;var Me=Object.getOwnPropertyNames;var Ke=Object.prototype.hasOwnProperty;var x=(o,e)=>()=>(o&&(e=o(o=0)),e);var I=(o,e)=>{for(var t in e)Fe(o,t,{get:e[t],enumerable:!0})},Ae=(o,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Me(e))!Ke.call(o,n)&&n!==t&&Fe(o,n,{get:()=>e[n],enumerable:!(i=_e(e,n))||i.enumerable});return o},p=(o,e,t)=>(Ae(o,e,"default"),t&&Ae(t,e,"default"));var Ce={};I(Ce,{__setTestEnvironment:()=>L,getENV:()=>d});function L(o){se=o}function je(){return typeof process<"u"&&process.versions&&process.versions.node?"node":typeof window<"u"||typeof self<"u"?"browser":"unknown"}function d(){return se||je()}var se,b=x(()=>{"use strict";se=null});var xe={};I(xe,{loadConfig:()=>N});import{z as M}from"zod";import{ShipError as pe}from"@shipstatic/types";function be(o){try{return Ye.parse(o)}catch(e){if(e instanceof M.ZodError){let t=e.issues[0],i=t.path.length>0?` at ${t.path.join(".")}`:"";throw pe.config(`Configuration validation failed${i}: ${t.message}`)}throw pe.config("Configuration validation failed")}}async function Xe(o){try{if(d()!=="node")return{};let{cosmiconfigSync:e}=await import("cosmiconfig"),t=await import("os"),i=e(ae,{searchPlaces:[`.${ae}rc`,"package.json",`${t.homedir()}/.${ae}rc`],stopDir:t.homedir()}),n;if(o?n=i.load(o):n=i.search(),n&&n.config)return be(n.config)}catch(e){if(e instanceof pe)throw e}return{}}async function N(o){if(d()!=="node")return{};let e={apiUrl:process.env.SHIP_API_URL,apiKey:process.env.SHIP_API_KEY,deployToken:process.env.SHIP_DEPLOY_TOKEN},t=await Xe(o),i={apiUrl:e.apiUrl??t.apiUrl,apiKey:e.apiKey??t.apiKey,deployToken:e.deployToken??t.deployToken};return be(i)}var ae,Ye,te=x(()=>{"use strict";b();ae="ship",Ye=M.object({apiUrl:M.string().url().optional(),apiKey:M.string().optional(),deployToken:M.string().optional()}).strict()});import{ShipError as U}from"@shipstatic/types";async function et(o){let e=(await import("spark-md5")).default;return new Promise((t,i)=>{let r=Math.ceil(o.size/2097152),s=0,m=new e.ArrayBuffer,u=new FileReader,P=()=>{let g=s*2097152,c=Math.min(g+2097152,o.size);u.readAsArrayBuffer(o.slice(g,c))};u.onload=g=>{let c=g.target?.result;if(!c){i(U.business("Failed to read file chunk"));return}m.append(c),s++,s<r?P():t({md5:m.end()})},u.onerror=()=>{i(U.business("Failed to calculate MD5: FileReader error"))},P()})}async function tt(o){let e=await import("crypto");if(Buffer.isBuffer(o)){let i=e.createHash("md5");return i.update(o),{md5:i.digest("hex")}}let t=await import("fs");return new Promise((i,n)=>{let r=e.createHash("md5"),s=t.createReadStream(o);s.on("error",m=>n(U.business(`Failed to read file for MD5: ${m.message}`))),s.on("data",m=>r.update(m)),s.on("end",()=>i({md5:r.digest("hex")}))})}async function E(o){let e=d();if(e==="browser"){if(!(o instanceof Blob))throw U.business("Invalid input for browser MD5 calculation: Expected Blob or File.");return et(o)}if(e==="node"){if(!(Buffer.isBuffer(o)||typeof o=="string"))throw U.business("Invalid input for Node.js MD5 calculation: Expected Buffer or file path string.");return tt(o)}throw U.business("Unknown or unsupported execution environment for MD5 calculation.")}var j=x(()=>{"use strict";b()});import{ShipError as it}from"@shipstatic/types";function V(o){ce=o}function T(){if(ce===null)throw it.config("Platform configuration not initialized. The SDK must fetch configuration from the API before performing operations.");return ce}var ce,W=x(()=>{"use strict";ce=null});import{isJunk as nt}from"junk";function k(o){return!o||o.length===0?[]:o.filter(e=>{if(!e)return!1;let t=e.replace(/\\/g,"/").split("/").filter(Boolean);if(t.length===0)return!0;let i=t[t.length-1];if(nt(i))return!1;let n=t.slice(0,-1);for(let r of n)if(oe.some(s=>r.toLowerCase()===s.toLowerCase()))return!1;return!0})}var oe,ie=x(()=>{"use strict";oe=["__MACOSX",".Trashes",".fseventsd",".Spotlight-V100"]});function Ee(o){if(!o||o.length===0)return"";let e=o.filter(r=>r&&typeof r=="string").map(r=>r.replace(/\\/g,"/"));if(e.length===0)return"";if(e.length===1)return e[0];let t=e.map(r=>r.split("/").filter(Boolean)),i=[],n=Math.min(...t.map(r=>r.length));for(let r=0;r<n;r++){let s=t[0][r];if(t.every(m=>m[r]===s))i.push(s);else break}return i.join("/")}function ne(o){return o.replace(/\\/g,"/").replace(/\/+/g,"/").replace(/^\/+/,"")}var ue=x(()=>{"use strict"});function O(o,e={}){if(e.flatten===!1)return o.map(i=>({path:ne(i),name:de(i)}));let t=rt(o);return o.map(i=>{let n=ne(i);if(t){let r=t.endsWith("/")?t:`${t}/`;n.startsWith(r)&&(n=n.substring(r.length))}return n||(n=de(i)),{path:n,name:de(i)}})}function rt(o){if(!o.length)return"";let t=o.map(r=>ne(r)).map(r=>r.split("/")),i=[],n=Math.min(...t.map(r=>r.length));for(let r=0;r<n-1;r++){let s=t[0][r];if(t.every(m=>m[r]===s))i.push(s);else break}return i.join("/")}function de(o){return o.split(/[/\\]/).pop()||o}var re=x(()=>{"use strict";ue()});import{ShipError as $}from"@shipstatic/types";import*as R from"fs";import*as F from"path";function Re(o){let e=[];try{let t=R.readdirSync(o);for(let i of t){let n=F.join(o,i),r=R.statSync(n);if(r.isDirectory()){let s=Re(n);e.push(...s)}else r.isFile()&&e.push(n)}}catch(t){console.error(`Error reading directory ${o}:`,t)}return e}async function Y(o,e={}){if(d()!=="node")throw $.business("processFilesForNode can only be called in Node.js environment.");let t=o.flatMap(f=>{let a=F.resolve(f);try{return R.statSync(a).isDirectory()?Re(a):[a]}catch{throw $.file(`Path does not exist: ${f}`,f)}}),i=[...new Set(t)],n=k(i);if(n.length===0)return[];let r=o.map(f=>F.resolve(f)),s=Ee(r.map(f=>{try{return R.statSync(f).isDirectory()?f:F.dirname(f)}catch{return F.dirname(f)}})),m=n.map(f=>{if(s&&s.length>0){let a=F.relative(s,f);if(a&&typeof a=="string"&&!a.startsWith(".."))return a.replace(/\\/g,"/")}return F.basename(f)}),u=O(m,{flatten:e.pathDetect!==!1}),P=[],g=0,c=T();for(let f=0;f<n.length;f++){let a=n[f],A=u[f].path;try{let w=R.statSync(a);if(w.size===0){console.warn(`Skipping empty file: ${a}`);continue}if(w.size>c.maxFileSize)throw $.business(`File ${a} is too large. Maximum allowed size is ${c.maxFileSize/(1024*1024)}MB.`);if(g+=w.size,g>c.maxTotalSize)throw $.business(`Total deploy size is too large. Maximum allowed is ${c.maxTotalSize/(1024*1024)}MB.`);let D=R.readFileSync(a),{md5:Le}=await E(D);if(A.includes("\0")||A.includes("/../")||A.startsWith("../")||A.endsWith("/.."))throw $.business(`Security error: Unsafe file path "${A}" for file: ${a}`);P.push({path:A,content:D,size:D.length,md5:Le})}catch(w){if(w instanceof $&&w.isClientError&&w.isClientError())throw w;console.error(`Could not process file ${a}:`,w)}}if(P.length>c.maxFilesCount)throw $.business(`Too many files to deploy. Maximum allowed is ${c.maxFilesCount} files.`);return P}var ge=x(()=>{"use strict";b();j();ie();W();re();ue()});import{ShipError as Te}from"@shipstatic/types";async function ke(o,e={}){let{getENV:t}=await Promise.resolve().then(()=>(b(),Ce));if(t()!=="browser")throw Te.business("processFilesForBrowser can only be called in a browser environment.");let i=Array.isArray(o)?o:Array.from(o),n=i.map(c=>c.webkitRelativePath||c.name),r=O(n,{flatten:e.pathDetect!==!1}),s=[];for(let c=0;c<i.length;c++){let f=i[c],a=r[c].path;if(a.includes("..")||a.includes("\0"))throw Te.business(`Security error: Unsafe file path "${a}" for file: ${f.name}`);s.push({file:f,relativePath:a})}let m=s.map(c=>c.relativePath),u=k(m),P=new Set(u),g=[];for(let c of s){if(!P.has(c.relativePath))continue;let{md5:f}=await E(c.file),a=await new Promise((A,w)=>{let D=new FileReader;D.onload=()=>A(D.result),D.onerror=()=>w(D.error),D.readAsArrayBuffer(c.file)});g.push({content:a,path:c.relativePath,size:c.file.size,md5:f})}return g}var Oe=x(()=>{"use strict";j();ie();re()});var Ue={};I(Ue,{convertBrowserInput:()=>Be,convertDeployInput:()=>st,convertNodeInput:()=>we});import{ShipError as C}from"@shipstatic/types";function $e(o,e={}){let t=T();if(!e.skipEmptyCheck&&o.length===0)throw C.business("No files to deploy.");if(o.length>t.maxFilesCount)throw C.business(`Too many files to deploy. Maximum allowed is ${t.maxFilesCount}.`);let i=0;for(let n of o){if(n.size>t.maxFileSize)throw C.business(`File ${n.name} is too large. Maximum allowed size is ${t.maxFileSize/(1024*1024)}MB.`);if(i+=n.size,i>t.maxTotalSize)throw C.business(`Total deploy size is too large. Maximum allowed is ${t.maxTotalSize/(1024*1024)}MB.`)}}function Ie(o,e){if(e==="node"){if(!Array.isArray(o))throw C.business("Invalid input type for Node.js environment. Expected string[] file paths.");if(o.length===0)throw C.business("No files to deploy.");if(!o.every(t=>typeof t=="string"))throw C.business("Invalid input type for Node.js environment. Expected string[] file paths.")}else if(e==="browser"&&o instanceof HTMLInputElement&&!o.files)throw C.business("No files selected in HTMLInputElement")}function Ne(o){let e=o.map(t=>({name:t.path,size:t.size}));return $e(e,{skipEmptyCheck:!0}),o.forEach(t=>{t.path&&(t.path=t.path.replace(/\\/g,"/"))}),o}async function we(o,e={}){Ie(o,"node");let t=await Y(o,e);return Ne(t)}async function Be(o,e={}){Ie(o,"browser");let t;if(o instanceof HTMLInputElement)t=Array.from(o.files);else if(typeof o=="object"&&o!==null&&typeof o.length=="number"&&typeof o.item=="function")t=Array.from(o);else if(Array.isArray(o)){if(o.length>0&&typeof o[0]=="string")throw C.business("Invalid input type for browser environment. Expected File[], FileList, or HTMLInputElement.");t=o}else throw C.business("Invalid input type for browser environment. Expected File[], FileList, or HTMLInputElement.");t=t.filter(n=>n.size===0?(console.warn(`Skipping empty file: ${n.name}`),!1):!0),$e(t);let i=await ke(t,e);return Ne(i)}async function st(o,e={},t){let i=d();if(i!=="node"&&i!=="browser")throw C.business("Unsupported execution environment.");let n;if(i==="node")if(typeof o=="string")n=await we([o],e);else if(Array.isArray(o)&&o.every(r=>typeof r=="string"))n=await we(o,e);else throw C.business("Invalid input type for Node.js environment. Expected string[] file paths.");else n=await Be(o,e);return n}var ze=x(()=>{"use strict";b();ge();Oe();W()});var Z={};I(Z,{ApiHttp:()=>v,DEFAULT_API:()=>fe,JUNK_DIRECTORIES:()=>oe,Ship:()=>X,ShipError:()=>ye,ShipErrorType:()=>he,__setTestEnvironment:()=>L,calculateMD5:()=>E,createAccountResource:()=>G,createAliasResource:()=>q,createDeploymentResource:()=>H,createKeysResource:()=>J,default:()=>Se,filterJunk:()=>k,getCurrentConfig:()=>T,getENV:()=>d,loadConfig:()=>N,mergeDeployOptions:()=>K,optimizeDeployPaths:()=>O,pluralize:()=>me,processFilesForNode:()=>Y,resolveConfig:()=>B,setConfig:()=>V});var y={};I(y,{ApiHttp:()=>v,DEFAULT_API:()=>fe,JUNK_DIRECTORIES:()=>oe,Ship:()=>X,ShipError:()=>ye,ShipErrorType:()=>he,__setTestEnvironment:()=>L,calculateMD5:()=>E,createAccountResource:()=>G,createAliasResource:()=>q,createDeploymentResource:()=>H,createKeysResource:()=>J,default:()=>Se,filterJunk:()=>k,getCurrentConfig:()=>T,getENV:()=>d,loadConfig:()=>N,mergeDeployOptions:()=>K,optimizeDeployPaths:()=>O,pluralize:()=>me,processFilesForNode:()=>Y,resolveConfig:()=>B,setConfig:()=>V});b();import*as ee from"mime-types";import{ShipError as S,DEFAULT_API as He}from"@shipstatic/types";var Q="/deployments",De="/ping",_="/aliases",qe="/config",Ge="/account",Je="/spa-check";function Ve(o){return typeof o=="string"?ee.lookup(o)||"application/octet-stream":ee.lookup(o.name)||o.type||"application/octet-stream"}async function We(o){let e=[];for await(let t of o)e.push(t);return e}var v=class{constructor(e){this.apiUrl=e.apiUrl||He,this.apiKey=e.apiKey??"",this.deployToken=e.deployToken??""}#t(e={}){let t={...e};return this.deployToken?t.Authorization=`Bearer ${this.deployToken}`:this.apiKey&&(t.Authorization=`Bearer ${this.apiKey}`),t}async#i(e,t={},i){let n=this.#t(t.headers),r={...t,headers:n,credentials:d()==="browser"?"include":"same-origin"};try{return await fetch(e,r)}catch(s){throw this.#o(s,i),s}}async#e(e,t={},i){try{let n=await this.#i(e,t,i);return n.ok||await this.#n(n,i),n.headers.get("Content-Length")==="0"||n.status===204?void 0:await n.json()}catch(n){throw n instanceof S||this.#o(n,i),n}}async ping(){return(await this.#e(`${this.apiUrl}${De}`,{method:"GET"},"Ping"))?.success||!1}async getPingResponse(){return await this.#e(`${this.apiUrl}${De}`,{method:"GET"},"Ping")}async getConfig(){return await this.#e(`${this.apiUrl}${qe}`,{method:"GET"},"Config")}async deploy(e,t={}){this.#r(e);let{apiUrl:i=this.apiUrl,signal:n,apiKey:r,deployToken:s}=t,{requestBody:m,requestHeaders:u}=await this.#s(e),P={};s?P={Authorization:`Bearer ${s}`}:r&&(P={Authorization:`Bearer ${r}`});let g={method:"POST",body:m,headers:{...u,...P},signal:n||null};return await this.#e(`${i}${Q}`,g,"Deploy")}#r(e){if(!e.length)throw S.business("No files to deploy.");for(let t of e)if(!t.md5)throw S.file(`MD5 checksum missing for file: ${t.path}`,t.path)}async#s(e){let t,i={};if(d()==="browser")t=this.#a(e);else if(d()==="node"){let{body:n,headers:r}=await this.#p(e);t=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength),i=r}else throw S.business("Unknown or unsupported execution environment");return{requestBody:t,requestHeaders:i}}#a(e){let t=new FormData,i=[];for(let n=0;n<e.length;n++){let r=e[n],s;if(r.content instanceof File||r.content instanceof Blob)s=r.content;else throw S.file(`Unsupported file.content type for browser FormData: ${r.path}`,r.path);let m=Ve(s instanceof File?s:r.path),u=new File([s],r.path,{type:m});t.append("files[]",u),i.push(r.md5)}return t.append("checksums",JSON.stringify(i)),t}async#p(e){let{FormData:t,File:i}=await import("formdata-node"),{FormDataEncoder:n}=await import("form-data-encoder"),r=await import("path"),s=new t,m=[];for(let f=0;f<e.length;f++){let a=e[f],A=ee.lookup(a.path)||"application/octet-stream",w;if(Buffer.isBuffer(a.content))w=new i([a.content],a.path,{type:A});else if(typeof Blob<"u"&&a.content instanceof Blob)w=new i([a.content],a.path,{type:A});else throw S.file(`Unsupported file.content type for Node.js FormData: ${a.path}`,a.path);let D=a.path.startsWith("/")?a.path:"/"+a.path;s.append("files[]",w,D),m.push(a.md5)}s.append("checksums",JSON.stringify(m));let u=new n(s),P=await We(u.encode()),g=Buffer.concat(P.map(f=>Buffer.from(f))),c={"Content-Type":u.contentType,"Content-Length":Buffer.byteLength(g).toString()};return{body:g,headers:c}}async#n(e,t){let i={};try{let n=e.headers.get("content-type");n&&n.includes("application/json")?i=await e.json():i={message:await e.text()}}catch{i={message:"Failed to parse error response"}}if(i.error||i.code||i.message){let n=i.message||i.error||`${t} failed due to API error`;throw e.status===401?S.authentication(n):S.api(n,e.status,i.code,i)}throw e.status===401?S.authentication(`Authentication failed for ${t}`):S.api(`${t} failed due to API error`,e.status,void 0,i)}#o(e,t){throw e.name==="AbortError"?S.cancelled(`${t} operation was cancelled.`):e instanceof TypeError&&e.message.includes("fetch")?S.network(`${t} failed due to network error: ${e.message}`,e):e instanceof S?e:S.business(`An unexpected error occurred during ${t}: ${e.message||"Unknown error"}`)}async listDeployments(){return await this.#e(`${this.apiUrl}${Q}`,{method:"GET"},"List Deployments")}async getDeployment(e){return await this.#e(`${this.apiUrl}${Q}/${e}`,{method:"GET"},"Get Deployment")}async removeDeployment(e){await this.#e(`${this.apiUrl}${Q}/${e}`,{method:"DELETE"},"Remove Deployment")}async setAlias(e,t){try{let i=await this.#i(`${this.apiUrl}${_}/${encodeURIComponent(e)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({deployment:t})},"Set Alias");return i.ok||await this.#n(i,"Set Alias"),{...await i.json(),isCreate:i.status===201}}catch(i){this.#o(i,"Set Alias")}}async getAlias(e){return await this.#e(`${this.apiUrl}${_}/${encodeURIComponent(e)}`,{method:"GET"},"Get Alias")}async listAliases(){return await this.#e(`${this.apiUrl}${_}`,{method:"GET"},"List Aliases")}async removeAlias(e){await this.#e(`${this.apiUrl}${_}/${encodeURIComponent(e)}`,{method:"DELETE"},"Remove Alias")}async checkAlias(e){return await this.#e(`${this.apiUrl}${_}/${encodeURIComponent(e)}/dns-check`,{method:"POST"},"Check Alias")}async getAccount(){return await this.#e(`${this.apiUrl}${Ge}`,{method:"GET"},"Get Account")}async createApiKey(){return await this.#e(`${this.apiUrl}/key`,{method:"POST"},"Create API Key")}async checkSPA(e){let t=e.find(u=>u.path==="index.html"||u.path==="/index.html");if(!t)return!1;let i=100*1024;if(t.size>i)return!1;let n;if(Buffer.isBuffer(t.content))n=t.content.toString("utf-8");else if(typeof Blob<"u"&&t.content instanceof Blob)n=await t.content.text();else if(typeof File<"u"&&t.content instanceof File)n=await t.content.text();else return!1;let s={files:e.map(u=>u.path),index:n};return(await this.#e(`${this.apiUrl}${Je}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)},"SPA Check")).isSPA}};b();import{DEFAULT_API as Ze}from"@shipstatic/types";async function Qe(o){let e=d();if(e==="browser")return{};if(e==="node"){let{loadConfig:t}=await Promise.resolve().then(()=>(te(),xe));return t(o)}else return{}}function B(o={},e={}){let t={apiUrl:o.apiUrl||e.apiUrl||Ze,apiKey:o.apiKey!==void 0?o.apiKey:e.apiKey,deployToken:o.deployToken!==void 0?o.deployToken:e.deployToken},i={apiUrl:t.apiUrl};return t.apiKey!==void 0&&(i.apiKey=t.apiKey),t.deployToken!==void 0&&(i.deployToken=t.deployToken),i}function K(o,e){let t={...o};return t.apiUrl===void 0&&e.apiUrl!==void 0&&(t.apiUrl=e.apiUrl),t.apiKey===void 0&&e.apiKey!==void 0&&(t.apiKey=e.apiKey),t.deployToken===void 0&&e.deployToken!==void 0&&(t.deployToken=e.deployToken),t.timeout===void 0&&e.timeout!==void 0&&(t.timeout=e.timeout),t.maxConcurrency===void 0&&e.maxConcurrency!==void 0&&(t.maxConcurrency=e.maxConcurrency),t.onProgress===void 0&&e.onProgress!==void 0&&(t.onProgress=e.onProgress),t.onProgressStats===void 0&&e.onProgressStats!==void 0&&(t.onProgressStats=e.onProgressStats),t}j();import{DEPLOYMENT_CONFIG_FILENAME as le}from"@shipstatic/types";async function ot(){let o={rewrites:[{source:"/(.*)",destination:"/index.html"}]},e=Buffer.from(JSON.stringify(o,null,2),"utf-8"),{md5:t}=await E(e);return{path:le,content:e,size:e.length,md5:t}}async function ve(o,e,t){if(t.spaDetect===!1||o.some(i=>i.path===le))return o;try{if(await e.checkSPA(o)){let n=await ot();return console.log(`SPA detected - generated ${le}`),[...o,n]}}catch{console.warn("SPA detection failed, continuing without auto-config")}return o}function H(o,e,t,i){return{create:async(n,r={})=>{t&&await t();let s=e?K(r,e):r,m=o();if(!i)throw new Error("processInput function is not provided.");let u=await i(n,s);return u=await ve(u,m,s),await m.deploy(u,s)},list:async()=>(t&&await t(),o().listDeployments()),remove:async n=>{t&&await t(),await o().removeDeployment(n)},get:async n=>(t&&await t(),o().getDeployment(n))}}function q(o,e){return{set:async(t,i)=>(e&&await e(),o().setAlias(t,i)),get:async t=>(e&&await e(),o().getAlias(t)),list:async()=>(e&&await e(),o().listAliases()),remove:async t=>{e&&await e(),await o().removeAlias(t)},check:async t=>(e&&await e(),o().checkAlias(t))}}function G(o,e){return{get:async()=>(e&&await e(),o().getAccount())}}function J(o,e){return{create:async()=>(e&&await e(),o().createApiKey())}}var z=class{constructor(e={}){this.initPromise=null;this.clientOptions=e;let t=this.resolveInitialConfig(e);this.http=new v({...e,...t});let i=()=>this.ensureInitialized(),n=()=>this.http;this._deployments=H(n,this.clientOptions,i,(r,s)=>this.processInput(r,s)),this._aliases=q(n,i),this._account=G(n,i),this._keys=J(n,i)}async ensureInitialized(){return this.initPromise||(this.initPromise=this.loadFullConfig()),this.initPromise}async ping(){return await this.ensureInitialized(),this.http.ping()}async deploy(e,t){return this.deployments.create(e,t)}async whoami(){return this.account.get()}get deployments(){return this._deployments}get aliases(){return this._aliases}get account(){return this._account}get keys(){return this._keys}};b();te();import{ShipError as Pe}from"@shipstatic/types";W();var l={};I(l,{ApiHttp:()=>v,DEFAULT_API:()=>fe,JUNK_DIRECTORIES:()=>oe,Ship:()=>z,ShipError:()=>ye,ShipErrorType:()=>he,__setTestEnvironment:()=>L,calculateMD5:()=>E,createAccountResource:()=>G,createAliasResource:()=>q,createDeploymentResource:()=>H,createKeysResource:()=>J,filterJunk:()=>k,getENV:()=>d,loadConfig:()=>Qe,mergeDeployOptions:()=>K,optimizeDeployPaths:()=>O,pluralize:()=>me,resolveConfig:()=>B});var h={};p(h,kt);import*as kt from"@shipstatic/types";p(l,h);import{DEFAULT_API as fe}from"@shipstatic/types";j();function me(o,e,t,i=!0){let n=o===1?e:t;return i?`${o} ${n}`:n}ie();re();b();import{ShipError as ye,ShipErrorType as he}from"@shipstatic/types";p(y,l);te();W();ge();b();var X=class extends z{constructor(e={}){if(d()!=="node")throw Pe.business("Node.js Ship class can only be used in Node.js environment.");super(e)}resolveInitialConfig(e){return B(e,{})}async loadFullConfig(){try{let e=await N(this.clientOptions.configFile),t=B(this.clientOptions,e);this.http=new v({...this.clientOptions,...t});let i=await this.http.getConfig();V(i)}catch(e){throw this.initPromise=null,e}}async processInput(e,t){if(!this.#t(e))throw Pe.business("Invalid input type for Node.js environment. Expected string[] file paths.");if(Array.isArray(e)&&e.length===0)throw Pe.business("No files to deploy.");let{convertDeployInput:i}=await Promise.resolve().then(()=>(ze(),Ue));return i(e,t,this.http)}#t(e){return typeof e=="string"?!0:Array.isArray(e)?e.every(t=>typeof t=="string"):!1}},Se=X;p(Z,y);export{v as ApiHttp,fe as DEFAULT_API,oe as JUNK_DIRECTORIES,X as Ship,ye as ShipError,he as ShipErrorType,L as __setTestEnvironment,E as calculateMD5,G as createAccountResource,q as createAliasResource,H as createDeploymentResource,J as createKeysResource,Se as default,k as filterJunk,T as getCurrentConfig,d as getENV,N as loadConfig,K as mergeDeployOptions,O as optimizeDeployPaths,me as pluralize,Y as processFilesForNode,B as resolveConfig,V as setConfig};
2
2
  //# sourceMappingURL=index.js.map