@giveitsmaller/sdk 0.6.0 → 0.7.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/_audit.js +60 -0
- package/dist/builder.d.ts +406 -0
- package/dist/builder.js +706 -0
- package/dist/client.d.ts +10 -0
- package/dist/client.js +28 -2
- package/dist/credentials.d.ts +61 -0
- package/dist/credentials.js +200 -0
- package/dist/ergonomic/preset_resolver.d.ts +75 -0
- package/dist/ergonomic/preset_resolver.js +568 -0
- package/dist/ergonomic/presets/_translate.d.ts +11 -0
- package/dist/ergonomic/presets/_translate.js +35 -0
- package/dist/ergonomic/presets/audio_compress.d.ts +16 -0
- package/dist/ergonomic/presets/audio_compress.js +45 -0
- package/dist/ergonomic/presets/document_epub_compress.d.ts +14 -0
- package/dist/ergonomic/presets/document_epub_compress.js +34 -0
- package/dist/ergonomic/presets/document_odf_compress.d.ts +14 -0
- package/dist/ergonomic/presets/document_odf_compress.js +34 -0
- package/dist/ergonomic/presets/document_office_compress.d.ts +16 -0
- package/dist/ergonomic/presets/document_office_compress.js +40 -0
- package/dist/ergonomic/presets/document_pdf_compress.d.ts +14 -0
- package/dist/ergonomic/presets/document_pdf_compress.js +35 -0
- package/dist/ergonomic/presets/image_compress.d.ts +43 -0
- package/dist/ergonomic/presets/image_compress.js +95 -0
- package/dist/ergonomic/presets/index.d.ts +77 -0
- package/dist/ergonomic/presets/index.js +216 -0
- package/dist/ergonomic/presets/video_compress.d.ts +30 -0
- package/dist/ergonomic/presets/video_compress.js +83 -0
- package/dist/errors.d.ts +147 -1
- package/dist/errors.js +161 -0
- package/dist/generated/sdk_spec/enums.d.ts +195 -0
- package/dist/generated/sdk_spec/enums.js +127 -0
- package/dist/generated/sdk_spec/errors.d.ts +16 -0
- package/dist/generated/sdk_spec/errors.js +473 -0
- package/dist/generated/sdk_spec/index.d.ts +4 -0
- package/dist/generated/sdk_spec/index.js +7 -0
- package/dist/generated/sdk_spec/presets.d.ts +6 -0
- package/dist/generated/sdk_spec/presets.js +157 -0
- package/dist/generated/sdk_spec/version.d.ts +3 -0
- package/dist/generated/sdk_spec/version.js +6 -0
- package/dist/gisl.d.ts +112 -0
- package/dist/gisl.js +266 -0
- package/dist/index.d.ts +15 -5
- package/dist/index.js +32 -4
- package/dist/merge.d.ts +142 -0
- package/dist/merge.js +411 -0
- package/dist/types.d.ts +12 -14
- package/dist/types.js +18 -0
- package/package.json +2 -2
package/dist/client.d.ts
CHANGED
|
@@ -197,6 +197,16 @@ export declare class GislClient {
|
|
|
197
197
|
* previous response to revalidate — a 304 surfaces as
|
|
198
198
|
* `{ notModified: true, etag, lastModified }` so callers can keep
|
|
199
199
|
* using their cached copy.
|
|
200
|
+
*
|
|
201
|
+
* NOTE: this schema describes available types/options/availability — it does
|
|
202
|
+
* NOT carry per-tier processing-class size/duration caps (the response shape
|
|
203
|
+
* has no `processing_class` / `per_tier_constraints`). The typed operation
|
|
204
|
+
* metadata's `AvailabilityEntry.constraints` is the conservative baseline
|
|
205
|
+
* only. Per-tier caps are enforced server-side: the caps that applied to an
|
|
206
|
+
* upload are reported on a successful `UploadResponse.constraintsApplied`,
|
|
207
|
+
* and an exceeded limit throws `GislUploadCapExceededError` (the 422 path
|
|
208
|
+
* carries a typed payload; the 413 absolute-cap path is a plain envelope).
|
|
209
|
+
* There is no read-ahead per-tier-cap API today.
|
|
200
210
|
*/
|
|
201
211
|
getSchema(options?: GetSchemaOptions): Promise<GetSchemaResult>;
|
|
202
212
|
/**
|
package/dist/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { open, stat } from 'node:fs/promises';
|
|
2
2
|
import { basename } from 'node:path';
|
|
3
|
-
import { AudioWatermarkDecodeRequestToJSON, AudioWatermarkDecodeResponseFromJSON, ExternalImportCreatedResponseFromJSON, ExternalImportRequestToJSON, LoginUser200ResponseDataFromJSON, CreditsBalanceResponseFromJSON, CreditsUsageResponseFromJSON, UploadResponseFromJSON, UploadProbeResponseFromJSON, MultipartInitiateResponseFromJSON, MultipartInitiateRequestMetadataHintToJSON, MultipartCompleteResponseFromJSON, MultipartCompleteRequestToJSON, WorkflowCancelResponseFromJSON, WorkflowCreateResponseFromJSON, WorkflowResumeResponseFromJSON, WorkflowStatusResponseFromJSON, WorkflowDownloadResponseFromJSON, MetadataResponseFromJSON, OperationsSchemaResponseFromJSON, RetryResponseFromJSON, WorkflowStatus, AuthErrorResponseFromJSON, AuthErrorType, BalanceExhaustedResponseFromJSON, BalanceExhaustedResponseRequiredActionEnum, FeatureNotAvailableResponseFromJSON, FeatureTierRestrictedResponseFromJSON, TierRestrictionKind, TierRestrictionResponseFromJSON, UserTier, WorkflowExpiredResponseFromJSON, UploadSizeExceedsTierResponseFromJSON, UploadDurationExceedsTierResponseFromJSON, UploadConstraintsAppliedProcessingClassPreAssignmentEnum, UploadThresholdsSingleShotMaxBytesEnum, UploadThresholdsMultipartChunkSizeEnum, UploadThresholdsMultipartConcurrencyDefaultEnum, } from '@giveitsmaller/contracts/openapi';
|
|
4
|
-
import { GislAbortError, GislApiError, GislAuthError, GislBalanceExhaustedError, GislError, GislFeatureNotAvailableError, GislFeatureTierRestrictedError, GislMultipartPartCountError, GislMultipartPartError, GislMultipartSessionNotFoundError, GislMultipartSessionOwnershipError, GislMultipartSessionAuthRequiredError, GislTierRestrictedError, GislTimeoutError, GislUploadCapExceededError, GislValidationError, GislWorkflowExpiredError, } from './errors.js';
|
|
3
|
+
import { AudioWatermarkDecodeRequestToJSON, AudioWatermarkDecodeResponseFromJSON, ExternalImportCreatedResponseFromJSON, ExternalImportRequestToJSON, LoginUser200ResponseDataFromJSON, CreditsBalanceResponseFromJSON, CreditsUsageResponseFromJSON, UploadResponseFromJSON, UploadProbeResponseFromJSON, MultipartInitiateResponseFromJSON, MultipartInitiateRequestMetadataHintToJSON, MultipartCompleteResponseFromJSON, MultipartCompleteRequestToJSON, WorkflowCancelResponseFromJSON, WorkflowCreateResponseFromJSON, WorkflowResumeResponseFromJSON, WorkflowStatusResponseFromJSON, WorkflowDownloadResponseFromJSON, MetadataResponseFromJSON, OperationsSchemaResponseFromJSON, RetryResponseFromJSON, WorkflowStatus, AuthErrorResponseFromJSON, AuthErrorType, BalanceExhaustedResponseFromJSON, BalanceExhaustedResponseRequiredActionEnum, FeatureNotAvailableResponseFromJSON, FeatureTierRestrictedResponseFromJSON, TierRestrictionKind, TierRestrictionResponseFromJSON, UserTier, WorkflowExpiredResponseFromJSON, ProbePendingResponseFromJSON, UploadSizeExceedsTierResponseFromJSON, UploadDurationExceedsTierResponseFromJSON, UploadConstraintsAppliedProcessingClassPreAssignmentEnum, UploadThresholdsSingleShotMaxBytesEnum, UploadThresholdsMultipartChunkSizeEnum, UploadThresholdsMultipartConcurrencyDefaultEnum, } from '@giveitsmaller/contracts/openapi';
|
|
4
|
+
import { GislAbortError, GislApiError, GislAuthError, GislBalanceExhaustedError, GislError, GislFeatureNotAvailableError, GislFeatureTierRestrictedError, GislMultipartPartCountError, GislMultipartPartError, GislMultipartSessionNotFoundError, GislMultipartSessionOwnershipError, GislMultipartSessionAuthRequiredError, GislTierRestrictedError, GislTimeoutError, GislProbePendingError, GislUploadCapExceededError, GislValidationError, GislWorkflowExpiredError, } from './errors.js';
|
|
5
5
|
import { parseSseStream } from './sse.js';
|
|
6
6
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
7
7
|
// SDK-internal aliases derived from the contract-pinned UploadThresholds enums
|
|
@@ -463,6 +463,22 @@ export class GislClient {
|
|
|
463
463
|
if (status === 422 && errorType === 'workflow_expired') {
|
|
464
464
|
tryThrowStructured(WorkflowExpiredResponseFromJSON, GislWorkflowExpiredError, (p) => isValidDate(p.expiredAt));
|
|
465
465
|
}
|
|
466
|
+
// Probe-pending 422 on POST /api/workflows (per contracts av1J0rEF).
|
|
467
|
+
// Recovery: caller polls /api/uploads/{id}/probe until terminal, then
|
|
468
|
+
// retries the workflow-create. `payload.jobRef` names which job.
|
|
469
|
+
//
|
|
470
|
+
// Defensive against v2.15.3 generator-bug: the openapi-generator-emitted
|
|
471
|
+
// `instanceOfProbePendingResponse` checks camelCase fields against the
|
|
472
|
+
// raw snake_case wire — fails dispatch when used at the CreateWorkflow
|
|
473
|
+
// 422 union top-level. We branch on the already-parsed `error_type`
|
|
474
|
+
// here (the SDK-side snake_case envelope read at line 659) and
|
|
475
|
+
// validate the payload shape directly on the raw JSON before
|
|
476
|
+
// ProbePendingResponseFromJSON converts snake_case → camelCase.
|
|
477
|
+
// Robust to BOTH the current v2.15.3 broken dispatch AND any future
|
|
478
|
+
// v2.15.4 fix that lands a discriminator.
|
|
479
|
+
if (status === 422 && errorType === 'probe_pending') {
|
|
480
|
+
tryThrowStructured(ProbePendingResponseFromJSON, GislProbePendingError, (p) => typeof p.jobRef === 'string' && p.jobRef.length > 0);
|
|
481
|
+
}
|
|
466
482
|
// Upload cap errors. `GislUploadCapExceededError` takes an extra `kind`
|
|
467
483
|
// arg so it cannot use `tryThrowStructured` (whose ErrorClass signature
|
|
468
484
|
// is fixed) — this local helper applies the SAME defense-in-depth
|
|
@@ -1680,6 +1696,16 @@ export class GislClient {
|
|
|
1680
1696
|
* previous response to revalidate — a 304 surfaces as
|
|
1681
1697
|
* `{ notModified: true, etag, lastModified }` so callers can keep
|
|
1682
1698
|
* using their cached copy.
|
|
1699
|
+
*
|
|
1700
|
+
* NOTE: this schema describes available types/options/availability — it does
|
|
1701
|
+
* NOT carry per-tier processing-class size/duration caps (the response shape
|
|
1702
|
+
* has no `processing_class` / `per_tier_constraints`). The typed operation
|
|
1703
|
+
* metadata's `AvailabilityEntry.constraints` is the conservative baseline
|
|
1704
|
+
* only. Per-tier caps are enforced server-side: the caps that applied to an
|
|
1705
|
+
* upload are reported on a successful `UploadResponse.constraintsApplied`,
|
|
1706
|
+
* and an exceeded limit throws `GislUploadCapExceededError` (the 422 path
|
|
1707
|
+
* carries a typed payload; the 413 absolute-cap path is a plain envelope).
|
|
1708
|
+
* There is no read-ahead per-tier-cap API today.
|
|
1683
1709
|
*/
|
|
1684
1710
|
async getSchema(options = {}) {
|
|
1685
1711
|
const params = new URLSearchParams();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential + endpoint resolution for the ergonomic-layer `gisl.create()`
|
|
3
|
+
* factory. Implements an AWS-style short-circuiting chain:
|
|
4
|
+
* API key: explicit arg → `GISL_API_KEY` env → `~/.gisl/credentials` profile
|
|
5
|
+
* Endpoint: explicit arg → `GISL_ENVIRONMENT` / `GISL_BASE_URL` env → prod default
|
|
6
|
+
*
|
|
7
|
+
* Sources are tried in order; the first hit wins. Malformed profile files
|
|
8
|
+
* surface as `GislConfigError` naming the offending profile (NEVER the value
|
|
9
|
+
* — credential values must not appear in error messages).
|
|
10
|
+
*
|
|
11
|
+
* Browser-safe: profile-file lookups dynamic-import `node:fs/promises` only
|
|
12
|
+
* when a profile path is requested AND a Node runtime is detected; missing
|
|
13
|
+
* fs in the browser yields the typed
|
|
14
|
+
* `GislConfigError('profile_unavailable_in_runtime')`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const GISL_API_KEY_ENV = "GISL_API_KEY";
|
|
17
|
+
export declare const GISL_BASE_URL_ENV = "GISL_BASE_URL";
|
|
18
|
+
export declare const GISL_ENVIRONMENT_ENV = "GISL_ENVIRONMENT";
|
|
19
|
+
/**
|
|
20
|
+
* Named environments → base URLs. Kept colocated with the resolver so the
|
|
21
|
+
* mapping table doesn't leak into `gisl.ts`.
|
|
22
|
+
*/
|
|
23
|
+
export declare const ENVIRONMENT_ENDPOINTS: {
|
|
24
|
+
readonly prod: "https://api.giveitsmaller.com";
|
|
25
|
+
readonly staging: "https://api.staging.giveitsmaller.com";
|
|
26
|
+
};
|
|
27
|
+
export type Environment = keyof typeof ENVIRONMENT_ENDPOINTS;
|
|
28
|
+
export declare const DEFAULT_ENDPOINT: "https://api.giveitsmaller.com";
|
|
29
|
+
export interface ResolveCredentialsOptions {
|
|
30
|
+
/** Explicit API key — highest precedence. */
|
|
31
|
+
readonly apiKey?: string;
|
|
32
|
+
/** Profile name from `~/.gisl/credentials`. Default: `'default'`. */
|
|
33
|
+
readonly profile?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Cookie-mode flag. When `true`, the caller is authenticating via
|
|
36
|
+
* session cookie (browser SPA flow) and a missing apiKey is NOT a
|
|
37
|
+
* configuration error — `resolveApiKey` returns `null` without
|
|
38
|
+
* throwing, and the ergonomic-layer factory accepts it.
|
|
39
|
+
*/
|
|
40
|
+
readonly useSessionCookie?: boolean;
|
|
41
|
+
/** Override the profile-file path (testing). */
|
|
42
|
+
readonly profilePath?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface ResolveEndpointOptions {
|
|
45
|
+
readonly baseUrl?: string;
|
|
46
|
+
readonly environment?: Environment;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the API key via the credential chain. Returns the resolved key,
|
|
50
|
+
* or `null` if no source produced one. The ergonomic-layer factory is
|
|
51
|
+
* responsible for deciding whether `null` is an error (default: yes, throw
|
|
52
|
+
* `GislMissingCredentialsError`) or acceptable (anonymous / cookie-mode).
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveApiKey(opts?: ResolveCredentialsOptions): Promise<string | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Resolve the base URL. Explicit `baseUrl` wins; otherwise an explicit
|
|
57
|
+
* `environment` name; otherwise the `GISL_BASE_URL` / `GISL_ENVIRONMENT`
|
|
58
|
+
* env vars; otherwise the prod default. Never throws — the chain always
|
|
59
|
+
* resolves to a usable URL.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveEndpoint(opts?: ResolveEndpointOptions): string;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential + endpoint resolution for the ergonomic-layer `gisl.create()`
|
|
3
|
+
* factory. Implements an AWS-style short-circuiting chain:
|
|
4
|
+
* API key: explicit arg → `GISL_API_KEY` env → `~/.gisl/credentials` profile
|
|
5
|
+
* Endpoint: explicit arg → `GISL_ENVIRONMENT` / `GISL_BASE_URL` env → prod default
|
|
6
|
+
*
|
|
7
|
+
* Sources are tried in order; the first hit wins. Malformed profile files
|
|
8
|
+
* surface as `GislConfigError` naming the offending profile (NEVER the value
|
|
9
|
+
* — credential values must not appear in error messages).
|
|
10
|
+
*
|
|
11
|
+
* Browser-safe: profile-file lookups dynamic-import `node:fs/promises` only
|
|
12
|
+
* when a profile path is requested AND a Node runtime is detected; missing
|
|
13
|
+
* fs in the browser yields the typed
|
|
14
|
+
* `GislConfigError('profile_unavailable_in_runtime')`.
|
|
15
|
+
*/
|
|
16
|
+
import { GislConfigError } from './errors.js';
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Environment constants
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
export const GISL_API_KEY_ENV = 'GISL_API_KEY';
|
|
21
|
+
export const GISL_BASE_URL_ENV = 'GISL_BASE_URL';
|
|
22
|
+
export const GISL_ENVIRONMENT_ENV = 'GISL_ENVIRONMENT';
|
|
23
|
+
/**
|
|
24
|
+
* Named environments → base URLs. Kept colocated with the resolver so the
|
|
25
|
+
* mapping table doesn't leak into `gisl.ts`.
|
|
26
|
+
*/
|
|
27
|
+
export const ENVIRONMENT_ENDPOINTS = {
|
|
28
|
+
prod: 'https://api.giveitsmaller.com',
|
|
29
|
+
staging: 'https://api.staging.giveitsmaller.com',
|
|
30
|
+
};
|
|
31
|
+
export const DEFAULT_ENDPOINT = ENVIRONMENT_ENDPOINTS.prod;
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Public resolvers
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
/**
|
|
36
|
+
* Resolve the API key via the credential chain. Returns the resolved key,
|
|
37
|
+
* or `null` if no source produced one. The ergonomic-layer factory is
|
|
38
|
+
* responsible for deciding whether `null` is an error (default: yes, throw
|
|
39
|
+
* `GislMissingCredentialsError`) or acceptable (anonymous / cookie-mode).
|
|
40
|
+
*/
|
|
41
|
+
export async function resolveApiKey(opts = {}) {
|
|
42
|
+
// 1. Explicit wins.
|
|
43
|
+
if (typeof opts.apiKey === 'string' && opts.apiKey.length > 0) {
|
|
44
|
+
return opts.apiKey;
|
|
45
|
+
}
|
|
46
|
+
// 2. Environment variable.
|
|
47
|
+
const envKey = readEnv(GISL_API_KEY_ENV);
|
|
48
|
+
if (envKey !== null && envKey.length > 0) {
|
|
49
|
+
return envKey;
|
|
50
|
+
}
|
|
51
|
+
// 3. Shared-config profile (`~/.gisl/credentials`). Skipped silently
|
|
52
|
+
// when running in a non-Node runtime (browser, edge) — the resolver
|
|
53
|
+
// returns `null` and the caller decides whether that's fatal.
|
|
54
|
+
if (!isNodeRuntime()) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const profileName = opts.profile ?? 'default';
|
|
58
|
+
const profilePath = opts.profilePath ?? defaultProfilePath();
|
|
59
|
+
if (profilePath === null) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const profileEntries = await readProfile(profilePath, profileName);
|
|
63
|
+
if (profileEntries === null) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const profileKey = profileEntries.api_key;
|
|
67
|
+
if (typeof profileKey === 'string' && profileKey.length > 0) {
|
|
68
|
+
return profileKey;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Resolve the base URL. Explicit `baseUrl` wins; otherwise an explicit
|
|
74
|
+
* `environment` name; otherwise the `GISL_BASE_URL` / `GISL_ENVIRONMENT`
|
|
75
|
+
* env vars; otherwise the prod default. Never throws — the chain always
|
|
76
|
+
* resolves to a usable URL.
|
|
77
|
+
*/
|
|
78
|
+
export function resolveEndpoint(opts = {}) {
|
|
79
|
+
if (typeof opts.baseUrl === 'string' && opts.baseUrl.length > 0) {
|
|
80
|
+
return opts.baseUrl;
|
|
81
|
+
}
|
|
82
|
+
if (typeof opts.environment === 'string') {
|
|
83
|
+
const explicitEnv = ENVIRONMENT_ENDPOINTS[opts.environment];
|
|
84
|
+
if (explicitEnv !== undefined) {
|
|
85
|
+
return explicitEnv;
|
|
86
|
+
}
|
|
87
|
+
// Codex r2 medium 23a17c1dbf75 — fail-closed on an unknown explicit
|
|
88
|
+
// environment name. A typo or JS-side caller previously silently fell
|
|
89
|
+
// through to env-var / prod default, which can mis-route a staging-
|
|
90
|
+
// intended request to production. The env-var path still allows
|
|
91
|
+
// unknown values to fall through (low-impact since it's an env config,
|
|
92
|
+
// not a code-level arg).
|
|
93
|
+
throw new GislConfigError(`Unknown environment '${opts.environment}'. Valid values: ${Object.keys(ENVIRONMENT_ENDPOINTS).join(', ')}.`);
|
|
94
|
+
}
|
|
95
|
+
const envBaseUrl = readEnv(GISL_BASE_URL_ENV);
|
|
96
|
+
if (envBaseUrl !== null && envBaseUrl.length > 0) {
|
|
97
|
+
return envBaseUrl;
|
|
98
|
+
}
|
|
99
|
+
const envEnvironment = readEnv(GISL_ENVIRONMENT_ENV);
|
|
100
|
+
if (envEnvironment !== null) {
|
|
101
|
+
const envMapped = ENVIRONMENT_ENDPOINTS[envEnvironment];
|
|
102
|
+
if (envMapped !== undefined) {
|
|
103
|
+
return envMapped;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return DEFAULT_ENDPOINT;
|
|
107
|
+
}
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Internals
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
function isNodeRuntime() {
|
|
112
|
+
return (typeof process !== 'undefined' &&
|
|
113
|
+
process.versions !== undefined &&
|
|
114
|
+
typeof process.versions.node === 'string');
|
|
115
|
+
}
|
|
116
|
+
function readEnv(name) {
|
|
117
|
+
if (!isNodeRuntime()) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const value = process.env[name];
|
|
121
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
122
|
+
}
|
|
123
|
+
function defaultProfilePath() {
|
|
124
|
+
const homeDir = readEnv('HOME') ?? readEnv('USERPROFILE');
|
|
125
|
+
if (homeDir === null) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
// Use forward-slash join — Node accepts it on every platform.
|
|
129
|
+
return `${homeDir}/.gisl/credentials`;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Read a single profile section from an INI-format credentials file.
|
|
133
|
+
* Returns `null` when the file does not exist; throws `GislConfigError`
|
|
134
|
+
* when the file exists but is malformed or the requested profile is
|
|
135
|
+
* absent. Never includes credential VALUES in error messages.
|
|
136
|
+
*/
|
|
137
|
+
async function readProfile(path, profileName) {
|
|
138
|
+
let raw;
|
|
139
|
+
try {
|
|
140
|
+
// Lazy import keeps `node:fs` out of browser bundles.
|
|
141
|
+
const fs = await import('node:fs/promises');
|
|
142
|
+
raw = await fs.readFile(path, 'utf8');
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
// ENOENT → no file = no credentials from this source (not an error).
|
|
146
|
+
const code = err.code;
|
|
147
|
+
if (code === 'ENOENT' || code === 'ENOTDIR') {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
throw new GislConfigError(`Failed to read shared credentials file at ${path}: ${code ?? 'unknown error'}`);
|
|
151
|
+
}
|
|
152
|
+
const parsed = parseIni(raw, path);
|
|
153
|
+
const entries = parsed[profileName];
|
|
154
|
+
if (entries === undefined) {
|
|
155
|
+
throw new GislConfigError(`Profile '${profileName}' not found in shared credentials file at ${path}`);
|
|
156
|
+
}
|
|
157
|
+
return entries;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Minimal INI parser sufficient for AWS-style credentials files: section
|
|
161
|
+
* headers `[name]`, `key = value` lines, `#` and `;` comments. Whitespace
|
|
162
|
+
* around `=` is trimmed. No nested sections, no interpolation, no quoting.
|
|
163
|
+
* Throws `GislConfigError` on malformed lines, naming only the LINE NUMBER
|
|
164
|
+
* and PROFILE — never the value.
|
|
165
|
+
*/
|
|
166
|
+
function parseIni(raw, sourcePath) {
|
|
167
|
+
const sections = {};
|
|
168
|
+
let currentSection = null;
|
|
169
|
+
const lines = raw.split(/\r?\n/);
|
|
170
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
171
|
+
const line = lines[i].trim();
|
|
172
|
+
if (line === '' || line.startsWith('#') || line.startsWith(';')) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (line.startsWith('[') && line.endsWith(']')) {
|
|
176
|
+
currentSection = line.slice(1, -1).trim();
|
|
177
|
+
if (currentSection === '') {
|
|
178
|
+
throw new GislConfigError(`Empty section header at line ${i + 1} in ${sourcePath}`);
|
|
179
|
+
}
|
|
180
|
+
if (sections[currentSection] === undefined) {
|
|
181
|
+
sections[currentSection] = {};
|
|
182
|
+
}
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (currentSection === null) {
|
|
186
|
+
throw new GislConfigError(`Key-value line at line ${i + 1} in ${sourcePath} is outside any section header`);
|
|
187
|
+
}
|
|
188
|
+
const eqIndex = line.indexOf('=');
|
|
189
|
+
if (eqIndex === -1) {
|
|
190
|
+
throw new GislConfigError(`Malformed line ${i + 1} in ${sourcePath} (expected key=value)`);
|
|
191
|
+
}
|
|
192
|
+
const key = line.slice(0, eqIndex).trim();
|
|
193
|
+
if (key === '') {
|
|
194
|
+
throw new GislConfigError(`Empty key at line ${i + 1} in ${sourcePath}`);
|
|
195
|
+
}
|
|
196
|
+
const value = line.slice(eqIndex + 1).trim();
|
|
197
|
+
sections[currentSection][key] = value;
|
|
198
|
+
}
|
|
199
|
+
return sections;
|
|
200
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ResolvedOptions } from '../builder.js';
|
|
2
|
+
import type { OptimizeFor } from '../generated/sdk_spec/enums.js';
|
|
3
|
+
import { type PresetDefaults, type PresetMedia, type PresetOp } from './presets/index.js';
|
|
4
|
+
/** Bumped on any change to a `*PresetOptions.shippedDefaultsFor(...)` cell value. */
|
|
5
|
+
export declare const PRESET_VERSION = "1.0";
|
|
6
|
+
/**
|
|
7
|
+
* Inputs to {@link resolveCompressOptions}. `media` selects which leaf
|
|
8
|
+
* DTO drives sdkDefault + clientDefault lookups + invalid-combo
|
|
9
|
+
* validations. `op` is currently always `'compress'`; future ops will
|
|
10
|
+
* extend the union.
|
|
11
|
+
*/
|
|
12
|
+
export interface ResolveCompressOptionsInput {
|
|
13
|
+
readonly media: PresetMedia;
|
|
14
|
+
readonly op: PresetOp;
|
|
15
|
+
/** Defaults registered via `gisl.create({ presetDefaults: ... })`. */
|
|
16
|
+
readonly presetDefaults?: PresetDefaults;
|
|
17
|
+
/**
|
|
18
|
+
* Scoped defaults attached via `client.withPresetDefaults(...)` (T4c —
|
|
19
|
+
* `ULAlOP6j`). Layered between `presetDefaults` and `presetOverrides`
|
|
20
|
+
* in the resolver chain. The derived client closes over this
|
|
21
|
+
* reference; `undefined` for clients that never went through a
|
|
22
|
+
* `withPresetDefaults` call.
|
|
23
|
+
*/
|
|
24
|
+
readonly scopedPresetDefaults?: PresetDefaults;
|
|
25
|
+
/** Per-call `presetOverrides` argument from the operation builder. */
|
|
26
|
+
readonly presetOverrides?: Readonly<Record<string, unknown>>;
|
|
27
|
+
/**
|
|
28
|
+
* `optimize` selects the preset level. When unset, NO shipped
|
|
29
|
+
* defaults apply (resolver layer 1 contributes nothing); the
|
|
30
|
+
* resulting `resolvedOptions.preset` is `null`.
|
|
31
|
+
*/
|
|
32
|
+
readonly optimize?: OptimizeFor;
|
|
33
|
+
/**
|
|
34
|
+
* Explicit per-call knobs the caller passed alongside `optimize` —
|
|
35
|
+
* e.g. `{ autoOrient: true }`. These are the highest-precedence
|
|
36
|
+
* layer.
|
|
37
|
+
*/
|
|
38
|
+
readonly explicitOptions: Readonly<Record<string, unknown>>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Output of {@link resolveCompressOptions}. `wireOptions` is the
|
|
42
|
+
* snake_case payload ready for the operation argument; `resolvedOptions`
|
|
43
|
+
* is the introspection projection surfaced on `Result.resolvedOptions`.
|
|
44
|
+
*/
|
|
45
|
+
export interface ResolveCompressOptionsOutput {
|
|
46
|
+
readonly wireOptions: Record<string, unknown>;
|
|
47
|
+
readonly resolvedOptions: ResolvedOptions;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Parse a `targetSize` value into a positive integer byte count.
|
|
51
|
+
*
|
|
52
|
+
* - Integer input passes through after non-negative + finite checks.
|
|
53
|
+
* - String input must match `<number><unit?>` with unit in
|
|
54
|
+
* `B|KB|MB|GB|TB` (case-insensitive). Multipliers are BINARY
|
|
55
|
+
* (1 KB = 1024). Decimal fractions allowed in the magnitude
|
|
56
|
+
* (`'1.5GB'` → `1.5 * 2^30` rounded down to integer bytes).
|
|
57
|
+
*
|
|
58
|
+
* Throws {@link GislConfigError} with `reason: 'invalid_target_size'`
|
|
59
|
+
* on any other input — including negative numbers, infinities, missing
|
|
60
|
+
* magnitude, unknown unit, or zero magnitude.
|
|
61
|
+
*
|
|
62
|
+
* @internal — exported for unit tests.
|
|
63
|
+
*/
|
|
64
|
+
export declare function _parseTargetSize(value: unknown): number;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve the wire payload + introspection projection for a compress
|
|
67
|
+
* operation call. Throws {@link GislConfigError} before any network
|
|
68
|
+
* round-trip when the merged options violate a documented constraint.
|
|
69
|
+
*
|
|
70
|
+
* Layers are applied in fixed order:
|
|
71
|
+
* SDK shipped → client default → scoped (T4c) → callPresetOverride → explicit.
|
|
72
|
+
*
|
|
73
|
+
* `optimize` unset ⇒ layer 1 contributes nothing; `resolvedOptions.preset = null`.
|
|
74
|
+
*/
|
|
75
|
+
export declare function resolveCompressOptions(input: ResolveCompressOptionsInput): ResolveCompressOptionsOutput;
|