@shipstatic/types 2.24.0-beta.1 → 2.24.0-beta.2
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 +57 -0
- package/dist/index.js +76 -0
- package/package.json +1 -1
- package/src/index.ts +100 -0
package/dist/index.d.ts
CHANGED
|
@@ -848,6 +848,14 @@ export declare const DEPLOY_FIELDS: {
|
|
|
848
848
|
readonly PRERENDER: "prerender";
|
|
849
849
|
/** @internal Server-processing flag — first-party `/upload` only. */
|
|
850
850
|
readonly SPA: "spa";
|
|
851
|
+
/**
|
|
852
|
+
* @internal The build settings, first-party `/upload` only and meaningful
|
|
853
|
+
* only with `BUILD`: the command to run instead of the manifest's build
|
|
854
|
+
* script, and the folder the site lands in. See {@link BUILD_SETTINGS}.
|
|
855
|
+
*/
|
|
856
|
+
readonly BUILD_COMMAND: "buildCommand";
|
|
857
|
+
/** @internal See {@link DEPLOY_FIELDS.BUILD_COMMAND}. */
|
|
858
|
+
readonly OUTPUT_DIR: "outputDir";
|
|
851
859
|
/** @internal reCAPTCHA proof — `web/www`'s public uploader only. */
|
|
852
860
|
readonly CAPTCHA: "captcha";
|
|
853
861
|
};
|
|
@@ -1847,6 +1855,20 @@ export interface DeploymentUploadOptions {
|
|
|
1847
1855
|
prerender?: boolean;
|
|
1848
1856
|
/** @internal Trigger server-side SPA detection. Only available via /upload endpoint. */
|
|
1849
1857
|
spa?: boolean;
|
|
1858
|
+
/**
|
|
1859
|
+
* @internal The command the build runs instead of the manifest's own
|
|
1860
|
+
* `build` script (`npm run build:site`, `hugo`, …). Only with `build`, only
|
|
1861
|
+
* via /upload; format in {@link BUILD_SETTINGS}, checked by
|
|
1862
|
+
* {@link validateBuildCommand}.
|
|
1863
|
+
*/
|
|
1864
|
+
buildCommand?: string;
|
|
1865
|
+
/**
|
|
1866
|
+
* @internal The folder the built site lands in (`public`, `dist/site`),
|
|
1867
|
+
* relative to the project root, for projects whose output the builder does
|
|
1868
|
+
* not find on its own. Only with `build`, only via /upload; format in
|
|
1869
|
+
* {@link BUILD_SETTINGS}, checked by {@link validateOutputDir}.
|
|
1870
|
+
*/
|
|
1871
|
+
outputDir?: string;
|
|
1850
1872
|
/** @internal reCAPTCHA proof for the anonymous human deploy channel. Only available via /upload endpoint. */
|
|
1851
1873
|
captcha?: string;
|
|
1852
1874
|
/**
|
|
@@ -2338,6 +2360,41 @@ export declare function serializeLabels(labels: string[] | undefined): string |
|
|
|
2338
2360
|
* @example deserializeLabels('') → []
|
|
2339
2361
|
*/
|
|
2340
2362
|
export declare function deserializeLabels(labelsJson: string | null): string[];
|
|
2363
|
+
/**
|
|
2364
|
+
* The format of the two per-deploy build settings a caller may name when the
|
|
2365
|
+
* builder's own detection is not enough: the command to run and the folder
|
|
2366
|
+
* the site lands in. Format rules only, the format/policy split this file
|
|
2367
|
+
* keeps everywhere: WHAT a value may look like lives here so both the console
|
|
2368
|
+
* (fast feedback) and the API (the boundary) refuse the same strings; whether
|
|
2369
|
+
* the command builds anything is the build's verdict, not a rule.
|
|
2370
|
+
*
|
|
2371
|
+
* Both are read only by first-party `/upload`, only with `build`, and both
|
|
2372
|
+
* run inside the throwaway container that already runs the project's own
|
|
2373
|
+
* arbitrary code, which is why the command's format is a shape rule (one
|
|
2374
|
+
* line, bounded) and not a safety rule. The folder's rule is what keeps it a
|
|
2375
|
+
* folder OF the project: relative, no parent segments, no leading slash.
|
|
2376
|
+
*/
|
|
2377
|
+
export declare const BUILD_SETTINGS: {
|
|
2378
|
+
/** A build command is one line, non-empty, and bounded. */
|
|
2379
|
+
readonly COMMAND_MAX_LENGTH: 200;
|
|
2380
|
+
/** An output folder is a bounded relative path. */
|
|
2381
|
+
readonly OUTPUT_DIR_MAX_LENGTH: 100;
|
|
2382
|
+
/** Path segments and separators only: `dist`, `dist/site`, `.output/public`. */
|
|
2383
|
+
readonly OUTPUT_DIR_PATTERN: RegExp;
|
|
2384
|
+
};
|
|
2385
|
+
/**
|
|
2386
|
+
* Validate an optional build command and return it normalized (trimmed).
|
|
2387
|
+
* Absent → `undefined`. Present → one line, 1 to
|
|
2388
|
+
* {@link BUILD_SETTINGS.COMMAND_MAX_LENGTH} characters, no control characters.
|
|
2389
|
+
*/
|
|
2390
|
+
export declare function validateBuildCommand(value: unknown): string | undefined;
|
|
2391
|
+
/**
|
|
2392
|
+
* Validate an optional output folder and return it normalized (trimmed, no
|
|
2393
|
+
* trailing slash). Absent → `undefined`. Present → a relative path of plain
|
|
2394
|
+
* segments, no `..`, no leading slash, at most
|
|
2395
|
+
* {@link BUILD_SETTINGS.OUTPUT_DIR_MAX_LENGTH} characters.
|
|
2396
|
+
*/
|
|
2397
|
+
export declare function validateOutputDir(value: unknown): string | undefined;
|
|
2341
2398
|
/**
|
|
2342
2399
|
* Length constraints for the optional deployment password
|
|
2343
2400
|
* (`DeploymentUploadOptions.password`). Single source of truth shared across
|
package/dist/index.js
CHANGED
|
@@ -310,6 +310,14 @@ export const DEPLOY_FIELDS = {
|
|
|
310
310
|
PRERENDER: 'prerender',
|
|
311
311
|
/** @internal Server-processing flag — first-party `/upload` only. */
|
|
312
312
|
SPA: 'spa',
|
|
313
|
+
/**
|
|
314
|
+
* @internal The build settings, first-party `/upload` only and meaningful
|
|
315
|
+
* only with `BUILD`: the command to run instead of the manifest's build
|
|
316
|
+
* script, and the folder the site lands in. See {@link BUILD_SETTINGS}.
|
|
317
|
+
*/
|
|
318
|
+
BUILD_COMMAND: 'buildCommand',
|
|
319
|
+
/** @internal See {@link DEPLOY_FIELDS.BUILD_COMMAND}. */
|
|
320
|
+
OUTPUT_DIR: 'outputDir',
|
|
313
321
|
/** @internal reCAPTCHA proof — `web/www`'s public uploader only. */
|
|
314
322
|
CAPTCHA: 'captcha',
|
|
315
323
|
};
|
|
@@ -1841,6 +1849,74 @@ export function deserializeLabels(labelsJson) {
|
|
|
1841
1849
|
}
|
|
1842
1850
|
}
|
|
1843
1851
|
// =============================================================================
|
|
1852
|
+
// BUILD SETTINGS
|
|
1853
|
+
// =============================================================================
|
|
1854
|
+
/**
|
|
1855
|
+
* The format of the two per-deploy build settings a caller may name when the
|
|
1856
|
+
* builder's own detection is not enough: the command to run and the folder
|
|
1857
|
+
* the site lands in. Format rules only, the format/policy split this file
|
|
1858
|
+
* keeps everywhere: WHAT a value may look like lives here so both the console
|
|
1859
|
+
* (fast feedback) and the API (the boundary) refuse the same strings; whether
|
|
1860
|
+
* the command builds anything is the build's verdict, not a rule.
|
|
1861
|
+
*
|
|
1862
|
+
* Both are read only by first-party `/upload`, only with `build`, and both
|
|
1863
|
+
* run inside the throwaway container that already runs the project's own
|
|
1864
|
+
* arbitrary code, which is why the command's format is a shape rule (one
|
|
1865
|
+
* line, bounded) and not a safety rule. The folder's rule is what keeps it a
|
|
1866
|
+
* folder OF the project: relative, no parent segments, no leading slash.
|
|
1867
|
+
*/
|
|
1868
|
+
export const BUILD_SETTINGS = {
|
|
1869
|
+
/** A build command is one line, non-empty, and bounded. */
|
|
1870
|
+
COMMAND_MAX_LENGTH: 200,
|
|
1871
|
+
/** An output folder is a bounded relative path. */
|
|
1872
|
+
OUTPUT_DIR_MAX_LENGTH: 100,
|
|
1873
|
+
/** Path segments and separators only: `dist`, `dist/site`, `.output/public`. */
|
|
1874
|
+
OUTPUT_DIR_PATTERN: /^[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/,
|
|
1875
|
+
};
|
|
1876
|
+
/**
|
|
1877
|
+
* Validate an optional build command and return it normalized (trimmed).
|
|
1878
|
+
* Absent → `undefined`. Present → one line, 1 to
|
|
1879
|
+
* {@link BUILD_SETTINGS.COMMAND_MAX_LENGTH} characters, no control characters.
|
|
1880
|
+
*/
|
|
1881
|
+
export function validateBuildCommand(value) {
|
|
1882
|
+
if (value === undefined || value === null || value === '')
|
|
1883
|
+
return undefined;
|
|
1884
|
+
if (typeof value !== 'string') {
|
|
1885
|
+
throw ShipError.validation('Build command must be a string');
|
|
1886
|
+
}
|
|
1887
|
+
const trimmed = value.trim();
|
|
1888
|
+
if (trimmed.length === 0 || trimmed.length > BUILD_SETTINGS.COMMAND_MAX_LENGTH) {
|
|
1889
|
+
throw ShipError.validation(`Build command must be between 1 and ${BUILD_SETTINGS.COMMAND_MAX_LENGTH} characters`);
|
|
1890
|
+
}
|
|
1891
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: a command is one line; a newline or any other control character is a second one
|
|
1892
|
+
if (/[\x00-\x1f\x7f]/.test(trimmed)) {
|
|
1893
|
+
throw ShipError.validation('Build command must be a single line');
|
|
1894
|
+
}
|
|
1895
|
+
return trimmed;
|
|
1896
|
+
}
|
|
1897
|
+
/**
|
|
1898
|
+
* Validate an optional output folder and return it normalized (trimmed, no
|
|
1899
|
+
* trailing slash). Absent → `undefined`. Present → a relative path of plain
|
|
1900
|
+
* segments, no `..`, no leading slash, at most
|
|
1901
|
+
* {@link BUILD_SETTINGS.OUTPUT_DIR_MAX_LENGTH} characters.
|
|
1902
|
+
*/
|
|
1903
|
+
export function validateOutputDir(value) {
|
|
1904
|
+
if (value === undefined || value === null || value === '')
|
|
1905
|
+
return undefined;
|
|
1906
|
+
if (typeof value !== 'string') {
|
|
1907
|
+
throw ShipError.validation('Output folder must be a string');
|
|
1908
|
+
}
|
|
1909
|
+
const trimmed = value.trim().replace(/\/+$/, '');
|
|
1910
|
+
if (trimmed.length === 0 || trimmed.length > BUILD_SETTINGS.OUTPUT_DIR_MAX_LENGTH) {
|
|
1911
|
+
throw ShipError.validation(`Output folder must be between 1 and ${BUILD_SETTINGS.OUTPUT_DIR_MAX_LENGTH} characters`);
|
|
1912
|
+
}
|
|
1913
|
+
if (!BUILD_SETTINGS.OUTPUT_DIR_PATTERN.test(trimmed) ||
|
|
1914
|
+
trimmed.split('/').some((segment) => segment === '..' || segment === '.')) {
|
|
1915
|
+
throw ShipError.validation('Output folder must be a relative path inside the project, like dist or dist/site');
|
|
1916
|
+
}
|
|
1917
|
+
return trimmed;
|
|
1918
|
+
}
|
|
1919
|
+
// =============================================================================
|
|
1844
1920
|
// PASSWORD UTILITIES
|
|
1845
1921
|
// =============================================================================
|
|
1846
1922
|
/**
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -932,6 +932,14 @@ export const DEPLOY_FIELDS = {
|
|
|
932
932
|
PRERENDER: 'prerender',
|
|
933
933
|
/** @internal Server-processing flag — first-party `/upload` only. */
|
|
934
934
|
SPA: 'spa',
|
|
935
|
+
/**
|
|
936
|
+
* @internal The build settings, first-party `/upload` only and meaningful
|
|
937
|
+
* only with `BUILD`: the command to run instead of the manifest's build
|
|
938
|
+
* script, and the folder the site lands in. See {@link BUILD_SETTINGS}.
|
|
939
|
+
*/
|
|
940
|
+
BUILD_COMMAND: 'buildCommand',
|
|
941
|
+
/** @internal See {@link DEPLOY_FIELDS.BUILD_COMMAND}. */
|
|
942
|
+
OUTPUT_DIR: 'outputDir',
|
|
935
943
|
/** @internal reCAPTCHA proof — `web/www`'s public uploader only. */
|
|
936
944
|
CAPTCHA: 'captcha',
|
|
937
945
|
} as const;
|
|
@@ -2705,6 +2713,20 @@ export interface DeploymentUploadOptions {
|
|
|
2705
2713
|
prerender?: boolean;
|
|
2706
2714
|
/** @internal Trigger server-side SPA detection. Only available via /upload endpoint. */
|
|
2707
2715
|
spa?: boolean;
|
|
2716
|
+
/**
|
|
2717
|
+
* @internal The command the build runs instead of the manifest's own
|
|
2718
|
+
* `build` script (`npm run build:site`, `hugo`, …). Only with `build`, only
|
|
2719
|
+
* via /upload; format in {@link BUILD_SETTINGS}, checked by
|
|
2720
|
+
* {@link validateBuildCommand}.
|
|
2721
|
+
*/
|
|
2722
|
+
buildCommand?: string;
|
|
2723
|
+
/**
|
|
2724
|
+
* @internal The folder the built site lands in (`public`, `dist/site`),
|
|
2725
|
+
* relative to the project root, for projects whose output the builder does
|
|
2726
|
+
* not find on its own. Only with `build`, only via /upload; format in
|
|
2727
|
+
* {@link BUILD_SETTINGS}, checked by {@link validateOutputDir}.
|
|
2728
|
+
*/
|
|
2729
|
+
outputDir?: string;
|
|
2708
2730
|
/** @internal reCAPTCHA proof for the anonymous human deploy channel. Only available via /upload endpoint. */
|
|
2709
2731
|
captcha?: string;
|
|
2710
2732
|
/**
|
|
@@ -3344,6 +3366,84 @@ export function deserializeLabels(labelsJson: string | null): string[] {
|
|
|
3344
3366
|
}
|
|
3345
3367
|
}
|
|
3346
3368
|
|
|
3369
|
+
// =============================================================================
|
|
3370
|
+
// BUILD SETTINGS
|
|
3371
|
+
// =============================================================================
|
|
3372
|
+
|
|
3373
|
+
/**
|
|
3374
|
+
* The format of the two per-deploy build settings a caller may name when the
|
|
3375
|
+
* builder's own detection is not enough: the command to run and the folder
|
|
3376
|
+
* the site lands in. Format rules only, the format/policy split this file
|
|
3377
|
+
* keeps everywhere: WHAT a value may look like lives here so both the console
|
|
3378
|
+
* (fast feedback) and the API (the boundary) refuse the same strings; whether
|
|
3379
|
+
* the command builds anything is the build's verdict, not a rule.
|
|
3380
|
+
*
|
|
3381
|
+
* Both are read only by first-party `/upload`, only with `build`, and both
|
|
3382
|
+
* run inside the throwaway container that already runs the project's own
|
|
3383
|
+
* arbitrary code, which is why the command's format is a shape rule (one
|
|
3384
|
+
* line, bounded) and not a safety rule. The folder's rule is what keeps it a
|
|
3385
|
+
* folder OF the project: relative, no parent segments, no leading slash.
|
|
3386
|
+
*/
|
|
3387
|
+
export const BUILD_SETTINGS = {
|
|
3388
|
+
/** A build command is one line, non-empty, and bounded. */
|
|
3389
|
+
COMMAND_MAX_LENGTH: 200,
|
|
3390
|
+
/** An output folder is a bounded relative path. */
|
|
3391
|
+
OUTPUT_DIR_MAX_LENGTH: 100,
|
|
3392
|
+
/** Path segments and separators only: `dist`, `dist/site`, `.output/public`. */
|
|
3393
|
+
OUTPUT_DIR_PATTERN: /^[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/,
|
|
3394
|
+
} as const;
|
|
3395
|
+
|
|
3396
|
+
/**
|
|
3397
|
+
* Validate an optional build command and return it normalized (trimmed).
|
|
3398
|
+
* Absent → `undefined`. Present → one line, 1 to
|
|
3399
|
+
* {@link BUILD_SETTINGS.COMMAND_MAX_LENGTH} characters, no control characters.
|
|
3400
|
+
*/
|
|
3401
|
+
export function validateBuildCommand(value: unknown): string | undefined {
|
|
3402
|
+
if (value === undefined || value === null || value === '') return undefined;
|
|
3403
|
+
if (typeof value !== 'string') {
|
|
3404
|
+
throw ShipError.validation('Build command must be a string');
|
|
3405
|
+
}
|
|
3406
|
+
const trimmed = value.trim();
|
|
3407
|
+
if (trimmed.length === 0 || trimmed.length > BUILD_SETTINGS.COMMAND_MAX_LENGTH) {
|
|
3408
|
+
throw ShipError.validation(
|
|
3409
|
+
`Build command must be between 1 and ${BUILD_SETTINGS.COMMAND_MAX_LENGTH} characters`,
|
|
3410
|
+
);
|
|
3411
|
+
}
|
|
3412
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: a command is one line; a newline or any other control character is a second one
|
|
3413
|
+
if (/[\x00-\x1f\x7f]/.test(trimmed)) {
|
|
3414
|
+
throw ShipError.validation('Build command must be a single line');
|
|
3415
|
+
}
|
|
3416
|
+
return trimmed;
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3419
|
+
/**
|
|
3420
|
+
* Validate an optional output folder and return it normalized (trimmed, no
|
|
3421
|
+
* trailing slash). Absent → `undefined`. Present → a relative path of plain
|
|
3422
|
+
* segments, no `..`, no leading slash, at most
|
|
3423
|
+
* {@link BUILD_SETTINGS.OUTPUT_DIR_MAX_LENGTH} characters.
|
|
3424
|
+
*/
|
|
3425
|
+
export function validateOutputDir(value: unknown): string | undefined {
|
|
3426
|
+
if (value === undefined || value === null || value === '') return undefined;
|
|
3427
|
+
if (typeof value !== 'string') {
|
|
3428
|
+
throw ShipError.validation('Output folder must be a string');
|
|
3429
|
+
}
|
|
3430
|
+
const trimmed = value.trim().replace(/\/+$/, '');
|
|
3431
|
+
if (trimmed.length === 0 || trimmed.length > BUILD_SETTINGS.OUTPUT_DIR_MAX_LENGTH) {
|
|
3432
|
+
throw ShipError.validation(
|
|
3433
|
+
`Output folder must be between 1 and ${BUILD_SETTINGS.OUTPUT_DIR_MAX_LENGTH} characters`,
|
|
3434
|
+
);
|
|
3435
|
+
}
|
|
3436
|
+
if (
|
|
3437
|
+
!BUILD_SETTINGS.OUTPUT_DIR_PATTERN.test(trimmed) ||
|
|
3438
|
+
trimmed.split('/').some((segment) => segment === '..' || segment === '.')
|
|
3439
|
+
) {
|
|
3440
|
+
throw ShipError.validation(
|
|
3441
|
+
'Output folder must be a relative path inside the project, like dist or dist/site',
|
|
3442
|
+
);
|
|
3443
|
+
}
|
|
3444
|
+
return trimmed;
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3347
3447
|
// =============================================================================
|
|
3348
3448
|
// PASSWORD UTILITIES
|
|
3349
3449
|
// =============================================================================
|