@nmakarov/cli-toolkit 0.46.0 → 0.47.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/s3.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/s3/index.js","../src/errors.js"],"sourcesContent":["/**\n * S3 component\n *\n * Thin async wrapper over @aws-sdk/client-s3 v3 with the cli-toolkit conventions:\n * const s3 = await S3.init(context);\n *\n * Supports multiple \"profiles\" (local, prod, stage, …) selected at runtime via\n * `--bucket=<profile>` (or `S3_BUCKET=<profile>`). Each profile has its own set\n * of env-suffixed parameters resolved by the framework, mirroring how `Db.init`\n * uses `dbName` to look up `dbConnectionString${Name}`.\n *\n * Profile selector:\n * --bucket=local (default)\n * --bucket=prod\n * --bucket=stage\n *\n * Per-profile parameters (all read via context.params; resolved from CLI,\n * env, .env files, defaults — see @nmakarov/cli-toolkit/params docs):\n * s3Bucket{Profile} string required\n * s3Region{Profile} string default us-east-1\n * s3Endpoint{Profile} string optional (set for MinIO/local; unset = real AWS)\n * s3ForcePathStyle{Profile} boolean default false (true for MinIO)\n * s3AccessKeyId{Profile} string optional (omit on EC2 to use instance profile)\n * s3SecretAccessKey{Profile} string optional\n *\n * Equivalent env vars are SCREAMING_SNAKE_CASE with the profile suffix:\n * S3_BUCKET_LOCAL, S3_REGION_LOCAL, S3_ENDPOINT_LOCAL,\n * S3_FORCE_PATH_STYLE_LOCAL, S3_ACCESS_KEY_ID_LOCAL, S3_SECRET_ACCESS_KEY_LOCAL\n *\n * Example (local with MinIO from docker-compose):\n * S3_BUCKET_LOCAL=photos-local\n * S3_REGION_LOCAL=us-east-1\n * S3_ENDPOINT_LOCAL=http://localhost:9000\n * S3_FORCE_PATH_STYLE_LOCAL=true\n * S3_ACCESS_KEY_ID_LOCAL=dev\n * S3_SECRET_ACCESS_KEY_LOCAL=devdevdev\n */\n\nimport {\n S3Client,\n HeadBucketCommand,\n HeadObjectCommand,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n CopyObjectCommand,\n ListObjectsV2Command,\n PutObjectTaggingCommand,\n GetObjectTaggingCommand,\n} from \"@aws-sdk/client-s3\";\n\nimport { ParamError } from \"../errors.js\";\n\nconst DEFAULT_PROFILE = \"local\";\n\nfunction capitalize(str) {\n if (!str) return str;\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nexport class S3 {\n /**\n * Build an S3 instance. Reads bucket profile from --bucket (default \"local\"),\n * then resolves per-profile params, builds the SDK client, and returns the\n * instance. Optionally pings the bucket once to verify reachability.\n */\n static async init(context, options = {}) {\n const profileDef = { bucket: \"string\" };\n const discovered = context?.params?.getAllForModule?.(\"s3\", profileDef) ?? {};\n const profile = options.bucket ?? discovered.bucket ?? DEFAULT_PROFILE;\n const cap = capitalize(profile);\n\n const config = {\n profile,\n bucketName: options.bucketName\n ?? await context.params.get(`s3Bucket${cap}`, \"string\"),\n region: options.region\n ?? await context.params.get(`s3Region${cap}`, \"string default us-east-1\"),\n endpoint: options.endpoint\n ?? await context.params.get(`s3Endpoint${cap}`, \"string\"),\n forcePathStyle: options.forcePathStyle\n ?? await context.params.get(`s3ForcePathStyle${cap}`, \"boolean default false\"),\n accessKeyId: options.accessKeyId\n ?? await context.params.get(`s3AccessKeyId${cap}`, \"string\"),\n secretAccessKey: options.secretAccessKey\n ?? await context.params.get(`s3SecretAccessKey${cap}`, \"string\"),\n };\n\n if (!config.bucketName) {\n throw new ParamError(\n `S3: bucket name not configured for profile \"${profile}\" `\n + `(set s3Bucket${cap} or S3_BUCKET_${profile.toUpperCase()})`\n );\n }\n\n const s3 = new S3(context, config);\n\n if (options.testBucket !== false) {\n try {\n await s3.bucketExists();\n context.logger?.debug?.(\n `[S3] profile=\"${profile}\" bucket=\"${config.bucketName}\" reachable`\n );\n } catch (err) {\n context.logger?.warn?.(\n `[S3] profile=\"${profile}\" bucket=\"${config.bucketName}\" `\n + `reachability test failed: ${err?.message ?? err}`\n );\n }\n }\n\n return s3;\n }\n\n constructor(context, config) {\n this.logger = context?.logger ?? console;\n this.profile = config.profile;\n this.bucketName = config.bucketName;\n this.region = config.region;\n this.endpoint = config.endpoint || null;\n\n const clientConfig = { region: config.region };\n if (config.endpoint) clientConfig.endpoint = config.endpoint;\n if (config.forcePathStyle) clientConfig.forcePathStyle = true;\n if (config.accessKeyId && config.secretAccessKey) {\n clientConfig.credentials = {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n };\n }\n\n // Silence the AWS SDK v3 \"node >=22 required after Jan 2027\" notice on\n // older Node. Honors an explicit env value if the user already set one.\n process.env.AWS_SDK_JS_NODE_VERSION_SUPPORT_WARNING_DISABLED ??= \"true\";\n\n this.client = new S3Client(clientConfig);\n }\n\n // ── info ────────────────────────────────────────────────────────────────\n getBucketName() { return this.bucketName; }\n getProfile() { return this.profile; }\n getRegion() { return this.region; }\n getEndpoint() { return this.endpoint; }\n\n // ── reachability ────────────────────────────────────────────────────────\n async bucketExists() {\n await this.client.send(new HeadBucketCommand({ Bucket: this.bucketName }));\n return true;\n }\n\n // ── HEAD / GET ──────────────────────────────────────────────────────────\n /** Returns null on 404; never throws for \"missing\". Other errors throw. */\n async headObject(key) {\n try {\n const out = await this.client.send(new HeadObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n return {\n etag: out.ETag,\n size: out.ContentLength,\n contentType: out.ContentType,\n lastModified: out.LastModified,\n metadata: out.Metadata,\n storageClass: out.StorageClass,\n };\n } catch (err) {\n if (this._isNotFound(err)) return null;\n throw err;\n }\n }\n\n /** Returns { body: Readable, contentType, contentLength, etag, ... } or null on 404. */\n async getObject(key) {\n try {\n const out = await this.client.send(new GetObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n return {\n body: out.Body,\n contentType: out.ContentType,\n contentLength: out.ContentLength,\n etag: out.ETag,\n lastModified: out.LastModified,\n metadata: out.Metadata,\n };\n } catch (err) {\n if (this._isNotFound(err)) return null;\n throw err;\n }\n }\n\n /** Buffers the whole object. Use only for small objects (manifests, JSON). */\n async getObjectBytes(key) {\n const obj = await this.getObject(key);\n if (!obj) return null;\n const chunks = [];\n for await (const chunk of obj.body) chunks.push(chunk);\n return { ...obj, body: Buffer.concat(chunks) };\n }\n\n /** Convenience for JSON manifests. Returns parsed object or null on 404. */\n async getJson(key) {\n const obj = await this.getObjectBytes(key);\n if (!obj) return null;\n return JSON.parse(obj.body.toString(\"utf8\"));\n }\n\n // ── PUT ─────────────────────────────────────────────────────────────────\n async putObject({ key, body, contentType, contentLength, tags, metadata }) {\n const cmd = new PutObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n Body: body,\n ...(contentType && { ContentType: contentType }),\n ...(contentLength != null && { ContentLength: contentLength }),\n ...(metadata && { Metadata: metadata }),\n ...(tags && { Tagging: this._tagsToQuery(tags) }),\n });\n return this.client.send(cmd);\n }\n\n /** Convenience for JSON manifests. */\n async putJson(key, value, opts = {}) {\n const json = JSON.stringify(value, null, opts.pretty ? 2 : 0);\n const body = Buffer.from(json, \"utf8\");\n return this.putObject({\n key,\n body,\n contentType: \"application/json\",\n contentLength: body.length,\n tags: opts.tags,\n metadata: opts.metadata,\n });\n }\n\n // ── DELETE ──────────────────────────────────────────────────────────────\n async deleteObject(key) {\n return this.client.send(new DeleteObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n }\n\n // ── COPY (for migration: legacy → new bucket, or intra-bucket \"rename\") ─\n async copyObject({ sourceBucket, sourceKey, key, contentType, metadata, tags }) {\n const src = sourceBucket || this.bucketName;\n const cmd = new CopyObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n CopySource: encodeURIComponent(`${src}/${sourceKey}`),\n ...(contentType && {\n ContentType: contentType,\n MetadataDirective: \"REPLACE\",\n }),\n ...(metadata && {\n Metadata: metadata,\n MetadataDirective: \"REPLACE\",\n }),\n ...(tags && {\n Tagging: this._tagsToQuery(tags),\n TaggingDirective: \"REPLACE\",\n }),\n });\n return this.client.send(cmd);\n }\n\n // ── LIST ────────────────────────────────────────────────────────────────\n async listObjects(prefix, { keysOnly = false, maxKeys = 1000, continuationToken } = {}) {\n const out = await this.client.send(new ListObjectsV2Command({\n Bucket: this.bucketName,\n Prefix: prefix,\n MaxKeys: maxKeys,\n ContinuationToken: continuationToken,\n }));\n const items = (out.Contents ?? []).map(o => ({\n key: o.Key,\n size: o.Size,\n etag: o.ETag,\n lastModified: o.LastModified,\n storageClass: o.StorageClass,\n }));\n return {\n items: keysOnly ? items.map(i => i.key) : items,\n isTruncated: !!out.IsTruncated,\n nextContinuationToken: out.NextContinuationToken,\n };\n }\n\n // ── TAGS (used for lifecycle rules, e.g. status=closed → Glacier IR) ────\n async putObjectTagging(key, tags) {\n return this.client.send(new PutObjectTaggingCommand({\n Bucket: this.bucketName,\n Key: key,\n Tagging: { TagSet: this._tagsToTagSet(tags) },\n }));\n }\n\n async getObjectTagging(key) {\n const out = await this.client.send(new GetObjectTaggingCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n const tags = {};\n for (const t of out.TagSet ?? []) tags[t.Key] = t.Value;\n return tags;\n }\n\n // ── internals ───────────────────────────────────────────────────────────\n _isNotFound(err) {\n const status = err?.$metadata?.httpStatusCode;\n return status === 404\n || err?.name === \"NotFound\"\n || err?.name === \"NoSuchKey\"\n || err?.Code === \"NoSuchKey\";\n }\n\n _tagsToQuery(tags) {\n return Object.entries(tags)\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)\n .join(\"&\");\n }\n\n _tagsToTagSet(tags) {\n return Object.entries(tags).map(([Key, Value]) => ({ Key, Value: String(Value) }));\n }\n}\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\nexport class HttpClientError extends FrameworkError {\n constructor(message, cause) {\n super(message);this.cause = cause;;\n this.name = \"HttpClientError\";\n }\n}\n\nexport class FileDatabaseError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"FileDatabaseError\";\n }\n}\n\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/s3/index.js","../src/errors.js"],"sourcesContent":["/**\n * S3 component\n *\n * Thin async wrapper over @aws-sdk/client-s3 v3 with the cli-toolkit conventions:\n * const s3 = await S3.init(context);\n *\n * Supports multiple \"profiles\" (local, production, stage, …) selected at runtime\n * via `--bucket=<profile>` (or `BUCKET` / env-suffixed `BUCKET_LOCAL`,\n * `BUCKET_PRODUCTION`). Each profile has its own set of parameters resolved by\n * the framework, mirroring how `Db.init` uses `dbName` → `dbConnectionString${Name}`.\n *\n * Profile selector (often driven by `--env` / `ENV` via Args env-suffixes):\n * --bucket=local (default when env=local)\n * --bucket=production (default when ENV=production and BUCKET_PRODUCTION=production)\n * --bucket=stage\n *\n * Per-profile parameters (CLI / env / .env — see @nmakarov/cli-toolkit/params):\n * s3Bucket{Profile} string required (S3_BUCKET_LOCAL, S3_BUCKET_PRODUCTION, …)\n * s3Region{Profile} string default us-east-1\n * s3Endpoint{Profile} string optional (MinIO/local; unset = real AWS)\n * s3ForcePathStyle{Profile} boolean default false (true for MinIO)\n * s3AccessKeyId{Profile} string optional (omit on EC2 for instance profile)\n * s3SecretAccessKey{Profile} string optional\n *\n * Example (.env — all profiles in one file):\n * BUCKET_LOCAL=local\n * BUCKET_PRODUCTION=production\n * S3_BUCKET_LOCAL=photos-local\n * S3_REGION_LOCAL=us-east-1\n * S3_ENDPOINT_LOCAL=http://localhost:9000\n * S3_FORCE_PATH_STYLE_LOCAL=true\n * S3_ACCESS_KEY_ID_LOCAL=dev\n * S3_SECRET_ACCESS_KEY_LOCAL=devdevdev\n * S3_BUCKET_PRODUCTION=photos-everystate-prod\n * S3_REGION_PRODUCTION=ca-central-1\n * …\n */\n\nimport {\n S3Client,\n HeadBucketCommand,\n HeadObjectCommand,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n CopyObjectCommand,\n ListObjectsV2Command,\n PutObjectTaggingCommand,\n GetObjectTaggingCommand,\n} from \"@aws-sdk/client-s3\";\n\nimport { ParamError } from \"../errors.js\";\n\nconst DEFAULT_PROFILE = \"local\";\n\nfunction capitalize(str) {\n if (!str) return str;\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nexport class S3 {\n /**\n * Build an S3 instance. Reads bucket profile from --bucket (default \"local\"),\n * then resolves per-profile params, builds the SDK client, and returns the\n * instance. Optionally pings the bucket once to verify reachability.\n */\n static async init(context, options = {}) {\n const profileDef = { bucket: \"string\" };\n const discovered = context?.params?.getAllForModule?.(\"s3\", profileDef) ?? {};\n const profile = options.bucket ?? discovered.bucket ?? DEFAULT_PROFILE;\n const cap = capitalize(profile);\n\n const config = {\n profile,\n bucketName: options.bucketName\n ?? await context.params.get(`s3Bucket${cap}`, \"string\"),\n region: options.region\n ?? await context.params.get(`s3Region${cap}`, \"string default us-east-1\"),\n endpoint: options.endpoint\n ?? await context.params.get(`s3Endpoint${cap}`, \"string\"),\n forcePathStyle: options.forcePathStyle\n ?? await context.params.get(`s3ForcePathStyle${cap}`, \"boolean default false\"),\n accessKeyId: options.accessKeyId\n ?? await context.params.get(`s3AccessKeyId${cap}`, \"string\"),\n secretAccessKey: options.secretAccessKey\n ?? await context.params.get(`s3SecretAccessKey${cap}`, \"string\"),\n };\n\n if (!config.bucketName) {\n throw new ParamError(\n `S3: bucket name not configured for profile \"${profile}\" `\n + `(set s3Bucket${cap} or S3_BUCKET_${profile.toUpperCase()})`\n );\n }\n\n const s3 = new S3(context, config);\n\n if (options.testBucket !== false) {\n try {\n await s3.bucketExists();\n context.logger?.debug?.(\n `[S3] profile=\"${profile}\" bucket=\"${config.bucketName}\" reachable`\n );\n } catch (err) {\n context.logger?.warn?.(\n `[S3] profile=\"${profile}\" bucket=\"${config.bucketName}\" `\n + `reachability test failed: ${err?.message ?? err}`\n );\n }\n }\n\n return s3;\n }\n\n constructor(context, config) {\n this.logger = context?.logger ?? console;\n this.profile = config.profile;\n this.bucketName = config.bucketName;\n this.region = config.region;\n this.endpoint = config.endpoint || null;\n\n const clientConfig = { region: config.region };\n if (config.endpoint) clientConfig.endpoint = config.endpoint;\n if (config.forcePathStyle) clientConfig.forcePathStyle = true;\n if (config.accessKeyId && config.secretAccessKey) {\n clientConfig.credentials = {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n };\n }\n\n // Silence the AWS SDK v3 \"node >=22 required after Jan 2027\" notice on\n // older Node. Honors an explicit env value if the user already set one.\n process.env.AWS_SDK_JS_NODE_VERSION_SUPPORT_WARNING_DISABLED ??= \"true\";\n\n this.client = new S3Client(clientConfig);\n }\n\n // ── info ────────────────────────────────────────────────────────────────\n getBucketName() { return this.bucketName; }\n getProfile() { return this.profile; }\n getRegion() { return this.region; }\n getEndpoint() { return this.endpoint; }\n\n // ── reachability ────────────────────────────────────────────────────────\n async bucketExists() {\n await this.client.send(new HeadBucketCommand({ Bucket: this.bucketName }));\n return true;\n }\n\n // ── HEAD / GET ──────────────────────────────────────────────────────────\n /** Returns null on 404; never throws for \"missing\". Other errors throw. */\n async headObject(key) {\n try {\n const out = await this.client.send(new HeadObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n return {\n etag: out.ETag,\n size: out.ContentLength,\n contentType: out.ContentType,\n lastModified: out.LastModified,\n metadata: out.Metadata,\n storageClass: out.StorageClass,\n };\n } catch (err) {\n if (this._isNotFound(err)) return null;\n throw err;\n }\n }\n\n /** Returns { body: Readable, contentType, contentLength, etag, ... } or null on 404. */\n async getObject(key) {\n try {\n const out = await this.client.send(new GetObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n return {\n body: out.Body,\n contentType: out.ContentType,\n contentLength: out.ContentLength,\n etag: out.ETag,\n lastModified: out.LastModified,\n metadata: out.Metadata,\n };\n } catch (err) {\n if (this._isNotFound(err)) return null;\n throw err;\n }\n }\n\n /** Buffers the whole object. Use only for small objects (manifests, JSON). */\n async getObjectBytes(key) {\n const obj = await this.getObject(key);\n if (!obj) return null;\n const chunks = [];\n for await (const chunk of obj.body) chunks.push(chunk);\n return { ...obj, body: Buffer.concat(chunks) };\n }\n\n /** Convenience for JSON manifests. Returns parsed object or null on 404. */\n async getJson(key) {\n const obj = await this.getObjectBytes(key);\n if (!obj) return null;\n return JSON.parse(obj.body.toString(\"utf8\"));\n }\n\n // ── PUT ─────────────────────────────────────────────────────────────────\n async putObject({ key, body, contentType, contentLength, tags, metadata }) {\n const cmd = new PutObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n Body: body,\n ...(contentType && { ContentType: contentType }),\n ...(contentLength != null && { ContentLength: contentLength }),\n ...(metadata && { Metadata: metadata }),\n ...(tags && { Tagging: this._tagsToQuery(tags) }),\n });\n return this.client.send(cmd);\n }\n\n /** Convenience for JSON manifests. */\n async putJson(key, value, opts = {}) {\n const json = JSON.stringify(value, null, opts.pretty ? 2 : 0);\n const body = Buffer.from(json, \"utf8\");\n return this.putObject({\n key,\n body,\n contentType: \"application/json\",\n contentLength: body.length,\n tags: opts.tags,\n metadata: opts.metadata,\n });\n }\n\n // ── DELETE ──────────────────────────────────────────────────────────────\n async deleteObject(key) {\n return this.client.send(new DeleteObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n }\n\n // ── COPY (for migration: legacy → new bucket, or intra-bucket \"rename\") ─\n async copyObject({ sourceBucket, sourceKey, key, contentType, metadata, tags }) {\n const src = sourceBucket || this.bucketName;\n const cmd = new CopyObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n CopySource: encodeURIComponent(`${src}/${sourceKey}`),\n ...(contentType && {\n ContentType: contentType,\n MetadataDirective: \"REPLACE\",\n }),\n ...(metadata && {\n Metadata: metadata,\n MetadataDirective: \"REPLACE\",\n }),\n ...(tags && {\n Tagging: this._tagsToQuery(tags),\n TaggingDirective: \"REPLACE\",\n }),\n });\n return this.client.send(cmd);\n }\n\n // ── LIST ────────────────────────────────────────────────────────────────\n async listObjects(prefix, { keysOnly = false, maxKeys = 1000, continuationToken } = {}) {\n const out = await this.client.send(new ListObjectsV2Command({\n Bucket: this.bucketName,\n Prefix: prefix,\n MaxKeys: maxKeys,\n ContinuationToken: continuationToken,\n }));\n const items = (out.Contents ?? []).map(o => ({\n key: o.Key,\n size: o.Size,\n etag: o.ETag,\n lastModified: o.LastModified,\n storageClass: o.StorageClass,\n }));\n return {\n items: keysOnly ? items.map(i => i.key) : items,\n isTruncated: !!out.IsTruncated,\n nextContinuationToken: out.NextContinuationToken,\n };\n }\n\n // ── TAGS (used for lifecycle rules, e.g. status=closed → Glacier IR) ────\n async putObjectTagging(key, tags) {\n return this.client.send(new PutObjectTaggingCommand({\n Bucket: this.bucketName,\n Key: key,\n Tagging: { TagSet: this._tagsToTagSet(tags) },\n }));\n }\n\n async getObjectTagging(key) {\n const out = await this.client.send(new GetObjectTaggingCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n const tags = {};\n for (const t of out.TagSet ?? []) tags[t.Key] = t.Value;\n return tags;\n }\n\n // ── internals ───────────────────────────────────────────────────────────\n _isNotFound(err) {\n const status = err?.$metadata?.httpStatusCode;\n return status === 404\n || err?.name === \"NotFound\"\n || err?.name === \"NoSuchKey\"\n || err?.Code === \"NoSuchKey\";\n }\n\n _tagsToQuery(tags) {\n return Object.entries(tags)\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)\n .join(\"&\");\n }\n\n _tagsToTagSet(tags) {\n return Object.entries(tags).map(([Key, Value]) => ({ Key, Value: String(Value) }));\n }\n}\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\nexport class HttpClientError extends FrameworkError {\n constructor(message, cause) {\n super(message);this.cause = cause;;\n this.name = \"HttpClientError\";\n }\n}\n\nexport class FileDatabaseError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"FileDatabaseError\";\n }\n}\n\n"],"mappings":"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","names":[]}
package/dist/s3.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/s3/index.js","../src/errors.js"],"sourcesContent":["/**\n * S3 component\n *\n * Thin async wrapper over @aws-sdk/client-s3 v3 with the cli-toolkit conventions:\n * const s3 = await S3.init(context);\n *\n * Supports multiple \"profiles\" (local, prod, stage, …) selected at runtime via\n * `--bucket=<profile>` (or `S3_BUCKET=<profile>`). Each profile has its own set\n * of env-suffixed parameters resolved by the framework, mirroring how `Db.init`\n * uses `dbName` to look up `dbConnectionString${Name}`.\n *\n * Profile selector:\n * --bucket=local (default)\n * --bucket=prod\n * --bucket=stage\n *\n * Per-profile parameters (all read via context.params; resolved from CLI,\n * env, .env files, defaults — see @nmakarov/cli-toolkit/params docs):\n * s3Bucket{Profile} string required\n * s3Region{Profile} string default us-east-1\n * s3Endpoint{Profile} string optional (set for MinIO/local; unset = real AWS)\n * s3ForcePathStyle{Profile} boolean default false (true for MinIO)\n * s3AccessKeyId{Profile} string optional (omit on EC2 to use instance profile)\n * s3SecretAccessKey{Profile} string optional\n *\n * Equivalent env vars are SCREAMING_SNAKE_CASE with the profile suffix:\n * S3_BUCKET_LOCAL, S3_REGION_LOCAL, S3_ENDPOINT_LOCAL,\n * S3_FORCE_PATH_STYLE_LOCAL, S3_ACCESS_KEY_ID_LOCAL, S3_SECRET_ACCESS_KEY_LOCAL\n *\n * Example (local with MinIO from docker-compose):\n * S3_BUCKET_LOCAL=photos-local\n * S3_REGION_LOCAL=us-east-1\n * S3_ENDPOINT_LOCAL=http://localhost:9000\n * S3_FORCE_PATH_STYLE_LOCAL=true\n * S3_ACCESS_KEY_ID_LOCAL=dev\n * S3_SECRET_ACCESS_KEY_LOCAL=devdevdev\n */\n\nimport {\n S3Client,\n HeadBucketCommand,\n HeadObjectCommand,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n CopyObjectCommand,\n ListObjectsV2Command,\n PutObjectTaggingCommand,\n GetObjectTaggingCommand,\n} from \"@aws-sdk/client-s3\";\n\nimport { ParamError } from \"../errors.js\";\n\nconst DEFAULT_PROFILE = \"local\";\n\nfunction capitalize(str) {\n if (!str) return str;\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nexport class S3 {\n /**\n * Build an S3 instance. Reads bucket profile from --bucket (default \"local\"),\n * then resolves per-profile params, builds the SDK client, and returns the\n * instance. Optionally pings the bucket once to verify reachability.\n */\n static async init(context, options = {}) {\n const profileDef = { bucket: \"string\" };\n const discovered = context?.params?.getAllForModule?.(\"s3\", profileDef) ?? {};\n const profile = options.bucket ?? discovered.bucket ?? DEFAULT_PROFILE;\n const cap = capitalize(profile);\n\n const config = {\n profile,\n bucketName: options.bucketName\n ?? await context.params.get(`s3Bucket${cap}`, \"string\"),\n region: options.region\n ?? await context.params.get(`s3Region${cap}`, \"string default us-east-1\"),\n endpoint: options.endpoint\n ?? await context.params.get(`s3Endpoint${cap}`, \"string\"),\n forcePathStyle: options.forcePathStyle\n ?? await context.params.get(`s3ForcePathStyle${cap}`, \"boolean default false\"),\n accessKeyId: options.accessKeyId\n ?? await context.params.get(`s3AccessKeyId${cap}`, \"string\"),\n secretAccessKey: options.secretAccessKey\n ?? await context.params.get(`s3SecretAccessKey${cap}`, \"string\"),\n };\n\n if (!config.bucketName) {\n throw new ParamError(\n `S3: bucket name not configured for profile \"${profile}\" `\n + `(set s3Bucket${cap} or S3_BUCKET_${profile.toUpperCase()})`\n );\n }\n\n const s3 = new S3(context, config);\n\n if (options.testBucket !== false) {\n try {\n await s3.bucketExists();\n context.logger?.debug?.(\n `[S3] profile=\"${profile}\" bucket=\"${config.bucketName}\" reachable`\n );\n } catch (err) {\n context.logger?.warn?.(\n `[S3] profile=\"${profile}\" bucket=\"${config.bucketName}\" `\n + `reachability test failed: ${err?.message ?? err}`\n );\n }\n }\n\n return s3;\n }\n\n constructor(context, config) {\n this.logger = context?.logger ?? console;\n this.profile = config.profile;\n this.bucketName = config.bucketName;\n this.region = config.region;\n this.endpoint = config.endpoint || null;\n\n const clientConfig = { region: config.region };\n if (config.endpoint) clientConfig.endpoint = config.endpoint;\n if (config.forcePathStyle) clientConfig.forcePathStyle = true;\n if (config.accessKeyId && config.secretAccessKey) {\n clientConfig.credentials = {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n };\n }\n\n // Silence the AWS SDK v3 \"node >=22 required after Jan 2027\" notice on\n // older Node. Honors an explicit env value if the user already set one.\n process.env.AWS_SDK_JS_NODE_VERSION_SUPPORT_WARNING_DISABLED ??= \"true\";\n\n this.client = new S3Client(clientConfig);\n }\n\n // ── info ────────────────────────────────────────────────────────────────\n getBucketName() { return this.bucketName; }\n getProfile() { return this.profile; }\n getRegion() { return this.region; }\n getEndpoint() { return this.endpoint; }\n\n // ── reachability ────────────────────────────────────────────────────────\n async bucketExists() {\n await this.client.send(new HeadBucketCommand({ Bucket: this.bucketName }));\n return true;\n }\n\n // ── HEAD / GET ──────────────────────────────────────────────────────────\n /** Returns null on 404; never throws for \"missing\". Other errors throw. */\n async headObject(key) {\n try {\n const out = await this.client.send(new HeadObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n return {\n etag: out.ETag,\n size: out.ContentLength,\n contentType: out.ContentType,\n lastModified: out.LastModified,\n metadata: out.Metadata,\n storageClass: out.StorageClass,\n };\n } catch (err) {\n if (this._isNotFound(err)) return null;\n throw err;\n }\n }\n\n /** Returns { body: Readable, contentType, contentLength, etag, ... } or null on 404. */\n async getObject(key) {\n try {\n const out = await this.client.send(new GetObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n return {\n body: out.Body,\n contentType: out.ContentType,\n contentLength: out.ContentLength,\n etag: out.ETag,\n lastModified: out.LastModified,\n metadata: out.Metadata,\n };\n } catch (err) {\n if (this._isNotFound(err)) return null;\n throw err;\n }\n }\n\n /** Buffers the whole object. Use only for small objects (manifests, JSON). */\n async getObjectBytes(key) {\n const obj = await this.getObject(key);\n if (!obj) return null;\n const chunks = [];\n for await (const chunk of obj.body) chunks.push(chunk);\n return { ...obj, body: Buffer.concat(chunks) };\n }\n\n /** Convenience for JSON manifests. Returns parsed object or null on 404. */\n async getJson(key) {\n const obj = await this.getObjectBytes(key);\n if (!obj) return null;\n return JSON.parse(obj.body.toString(\"utf8\"));\n }\n\n // ── PUT ─────────────────────────────────────────────────────────────────\n async putObject({ key, body, contentType, contentLength, tags, metadata }) {\n const cmd = new PutObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n Body: body,\n ...(contentType && { ContentType: contentType }),\n ...(contentLength != null && { ContentLength: contentLength }),\n ...(metadata && { Metadata: metadata }),\n ...(tags && { Tagging: this._tagsToQuery(tags) }),\n });\n return this.client.send(cmd);\n }\n\n /** Convenience for JSON manifests. */\n async putJson(key, value, opts = {}) {\n const json = JSON.stringify(value, null, opts.pretty ? 2 : 0);\n const body = Buffer.from(json, \"utf8\");\n return this.putObject({\n key,\n body,\n contentType: \"application/json\",\n contentLength: body.length,\n tags: opts.tags,\n metadata: opts.metadata,\n });\n }\n\n // ── DELETE ──────────────────────────────────────────────────────────────\n async deleteObject(key) {\n return this.client.send(new DeleteObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n }\n\n // ── COPY (for migration: legacy → new bucket, or intra-bucket \"rename\") ─\n async copyObject({ sourceBucket, sourceKey, key, contentType, metadata, tags }) {\n const src = sourceBucket || this.bucketName;\n const cmd = new CopyObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n CopySource: encodeURIComponent(`${src}/${sourceKey}`),\n ...(contentType && {\n ContentType: contentType,\n MetadataDirective: \"REPLACE\",\n }),\n ...(metadata && {\n Metadata: metadata,\n MetadataDirective: \"REPLACE\",\n }),\n ...(tags && {\n Tagging: this._tagsToQuery(tags),\n TaggingDirective: \"REPLACE\",\n }),\n });\n return this.client.send(cmd);\n }\n\n // ── LIST ────────────────────────────────────────────────────────────────\n async listObjects(prefix, { keysOnly = false, maxKeys = 1000, continuationToken } = {}) {\n const out = await this.client.send(new ListObjectsV2Command({\n Bucket: this.bucketName,\n Prefix: prefix,\n MaxKeys: maxKeys,\n ContinuationToken: continuationToken,\n }));\n const items = (out.Contents ?? []).map(o => ({\n key: o.Key,\n size: o.Size,\n etag: o.ETag,\n lastModified: o.LastModified,\n storageClass: o.StorageClass,\n }));\n return {\n items: keysOnly ? items.map(i => i.key) : items,\n isTruncated: !!out.IsTruncated,\n nextContinuationToken: out.NextContinuationToken,\n };\n }\n\n // ── TAGS (used for lifecycle rules, e.g. status=closed → Glacier IR) ────\n async putObjectTagging(key, tags) {\n return this.client.send(new PutObjectTaggingCommand({\n Bucket: this.bucketName,\n Key: key,\n Tagging: { TagSet: this._tagsToTagSet(tags) },\n }));\n }\n\n async getObjectTagging(key) {\n const out = await this.client.send(new GetObjectTaggingCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n const tags = {};\n for (const t of out.TagSet ?? []) tags[t.Key] = t.Value;\n return tags;\n }\n\n // ── internals ───────────────────────────────────────────────────────────\n _isNotFound(err) {\n const status = err?.$metadata?.httpStatusCode;\n return status === 404\n || err?.name === \"NotFound\"\n || err?.name === \"NoSuchKey\"\n || err?.Code === \"NoSuchKey\";\n }\n\n _tagsToQuery(tags) {\n return Object.entries(tags)\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)\n .join(\"&\");\n }\n\n _tagsToTagSet(tags) {\n return Object.entries(tags).map(([Key, Value]) => ({ Key, Value: String(Value) }));\n }\n}\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\nexport class HttpClientError extends FrameworkError {\n constructor(message, cause) {\n super(message);this.cause = cause;;\n this.name = \"HttpClientError\";\n }\n}\n\nexport class FileDatabaseError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"FileDatabaseError\";\n }\n}\n\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/s3/index.js","../src/errors.js"],"sourcesContent":["/**\n * S3 component\n *\n * Thin async wrapper over @aws-sdk/client-s3 v3 with the cli-toolkit conventions:\n * const s3 = await S3.init(context);\n *\n * Supports multiple \"profiles\" (local, production, stage, …) selected at runtime\n * via `--bucket=<profile>` (or `BUCKET` / env-suffixed `BUCKET_LOCAL`,\n * `BUCKET_PRODUCTION`). Each profile has its own set of parameters resolved by\n * the framework, mirroring how `Db.init` uses `dbName` → `dbConnectionString${Name}`.\n *\n * Profile selector (often driven by `--env` / `ENV` via Args env-suffixes):\n * --bucket=local (default when env=local)\n * --bucket=production (default when ENV=production and BUCKET_PRODUCTION=production)\n * --bucket=stage\n *\n * Per-profile parameters (CLI / env / .env — see @nmakarov/cli-toolkit/params):\n * s3Bucket{Profile} string required (S3_BUCKET_LOCAL, S3_BUCKET_PRODUCTION, …)\n * s3Region{Profile} string default us-east-1\n * s3Endpoint{Profile} string optional (MinIO/local; unset = real AWS)\n * s3ForcePathStyle{Profile} boolean default false (true for MinIO)\n * s3AccessKeyId{Profile} string optional (omit on EC2 for instance profile)\n * s3SecretAccessKey{Profile} string optional\n *\n * Example (.env — all profiles in one file):\n * BUCKET_LOCAL=local\n * BUCKET_PRODUCTION=production\n * S3_BUCKET_LOCAL=photos-local\n * S3_REGION_LOCAL=us-east-1\n * S3_ENDPOINT_LOCAL=http://localhost:9000\n * S3_FORCE_PATH_STYLE_LOCAL=true\n * S3_ACCESS_KEY_ID_LOCAL=dev\n * S3_SECRET_ACCESS_KEY_LOCAL=devdevdev\n * S3_BUCKET_PRODUCTION=photos-everystate-prod\n * S3_REGION_PRODUCTION=ca-central-1\n * …\n */\n\nimport {\n S3Client,\n HeadBucketCommand,\n HeadObjectCommand,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n CopyObjectCommand,\n ListObjectsV2Command,\n PutObjectTaggingCommand,\n GetObjectTaggingCommand,\n} from \"@aws-sdk/client-s3\";\n\nimport { ParamError } from \"../errors.js\";\n\nconst DEFAULT_PROFILE = \"local\";\n\nfunction capitalize(str) {\n if (!str) return str;\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nexport class S3 {\n /**\n * Build an S3 instance. Reads bucket profile from --bucket (default \"local\"),\n * then resolves per-profile params, builds the SDK client, and returns the\n * instance. Optionally pings the bucket once to verify reachability.\n */\n static async init(context, options = {}) {\n const profileDef = { bucket: \"string\" };\n const discovered = context?.params?.getAllForModule?.(\"s3\", profileDef) ?? {};\n const profile = options.bucket ?? discovered.bucket ?? DEFAULT_PROFILE;\n const cap = capitalize(profile);\n\n const config = {\n profile,\n bucketName: options.bucketName\n ?? await context.params.get(`s3Bucket${cap}`, \"string\"),\n region: options.region\n ?? await context.params.get(`s3Region${cap}`, \"string default us-east-1\"),\n endpoint: options.endpoint\n ?? await context.params.get(`s3Endpoint${cap}`, \"string\"),\n forcePathStyle: options.forcePathStyle\n ?? await context.params.get(`s3ForcePathStyle${cap}`, \"boolean default false\"),\n accessKeyId: options.accessKeyId\n ?? await context.params.get(`s3AccessKeyId${cap}`, \"string\"),\n secretAccessKey: options.secretAccessKey\n ?? await context.params.get(`s3SecretAccessKey${cap}`, \"string\"),\n };\n\n if (!config.bucketName) {\n throw new ParamError(\n `S3: bucket name not configured for profile \"${profile}\" `\n + `(set s3Bucket${cap} or S3_BUCKET_${profile.toUpperCase()})`\n );\n }\n\n const s3 = new S3(context, config);\n\n if (options.testBucket !== false) {\n try {\n await s3.bucketExists();\n context.logger?.debug?.(\n `[S3] profile=\"${profile}\" bucket=\"${config.bucketName}\" reachable`\n );\n } catch (err) {\n context.logger?.warn?.(\n `[S3] profile=\"${profile}\" bucket=\"${config.bucketName}\" `\n + `reachability test failed: ${err?.message ?? err}`\n );\n }\n }\n\n return s3;\n }\n\n constructor(context, config) {\n this.logger = context?.logger ?? console;\n this.profile = config.profile;\n this.bucketName = config.bucketName;\n this.region = config.region;\n this.endpoint = config.endpoint || null;\n\n const clientConfig = { region: config.region };\n if (config.endpoint) clientConfig.endpoint = config.endpoint;\n if (config.forcePathStyle) clientConfig.forcePathStyle = true;\n if (config.accessKeyId && config.secretAccessKey) {\n clientConfig.credentials = {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n };\n }\n\n // Silence the AWS SDK v3 \"node >=22 required after Jan 2027\" notice on\n // older Node. Honors an explicit env value if the user already set one.\n process.env.AWS_SDK_JS_NODE_VERSION_SUPPORT_WARNING_DISABLED ??= \"true\";\n\n this.client = new S3Client(clientConfig);\n }\n\n // ── info ────────────────────────────────────────────────────────────────\n getBucketName() { return this.bucketName; }\n getProfile() { return this.profile; }\n getRegion() { return this.region; }\n getEndpoint() { return this.endpoint; }\n\n // ── reachability ────────────────────────────────────────────────────────\n async bucketExists() {\n await this.client.send(new HeadBucketCommand({ Bucket: this.bucketName }));\n return true;\n }\n\n // ── HEAD / GET ──────────────────────────────────────────────────────────\n /** Returns null on 404; never throws for \"missing\". Other errors throw. */\n async headObject(key) {\n try {\n const out = await this.client.send(new HeadObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n return {\n etag: out.ETag,\n size: out.ContentLength,\n contentType: out.ContentType,\n lastModified: out.LastModified,\n metadata: out.Metadata,\n storageClass: out.StorageClass,\n };\n } catch (err) {\n if (this._isNotFound(err)) return null;\n throw err;\n }\n }\n\n /** Returns { body: Readable, contentType, contentLength, etag, ... } or null on 404. */\n async getObject(key) {\n try {\n const out = await this.client.send(new GetObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n return {\n body: out.Body,\n contentType: out.ContentType,\n contentLength: out.ContentLength,\n etag: out.ETag,\n lastModified: out.LastModified,\n metadata: out.Metadata,\n };\n } catch (err) {\n if (this._isNotFound(err)) return null;\n throw err;\n }\n }\n\n /** Buffers the whole object. Use only for small objects (manifests, JSON). */\n async getObjectBytes(key) {\n const obj = await this.getObject(key);\n if (!obj) return null;\n const chunks = [];\n for await (const chunk of obj.body) chunks.push(chunk);\n return { ...obj, body: Buffer.concat(chunks) };\n }\n\n /** Convenience for JSON manifests. Returns parsed object or null on 404. */\n async getJson(key) {\n const obj = await this.getObjectBytes(key);\n if (!obj) return null;\n return JSON.parse(obj.body.toString(\"utf8\"));\n }\n\n // ── PUT ─────────────────────────────────────────────────────────────────\n async putObject({ key, body, contentType, contentLength, tags, metadata }) {\n const cmd = new PutObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n Body: body,\n ...(contentType && { ContentType: contentType }),\n ...(contentLength != null && { ContentLength: contentLength }),\n ...(metadata && { Metadata: metadata }),\n ...(tags && { Tagging: this._tagsToQuery(tags) }),\n });\n return this.client.send(cmd);\n }\n\n /** Convenience for JSON manifests. */\n async putJson(key, value, opts = {}) {\n const json = JSON.stringify(value, null, opts.pretty ? 2 : 0);\n const body = Buffer.from(json, \"utf8\");\n return this.putObject({\n key,\n body,\n contentType: \"application/json\",\n contentLength: body.length,\n tags: opts.tags,\n metadata: opts.metadata,\n });\n }\n\n // ── DELETE ──────────────────────────────────────────────────────────────\n async deleteObject(key) {\n return this.client.send(new DeleteObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n }\n\n // ── COPY (for migration: legacy → new bucket, or intra-bucket \"rename\") ─\n async copyObject({ sourceBucket, sourceKey, key, contentType, metadata, tags }) {\n const src = sourceBucket || this.bucketName;\n const cmd = new CopyObjectCommand({\n Bucket: this.bucketName,\n Key: key,\n CopySource: encodeURIComponent(`${src}/${sourceKey}`),\n ...(contentType && {\n ContentType: contentType,\n MetadataDirective: \"REPLACE\",\n }),\n ...(metadata && {\n Metadata: metadata,\n MetadataDirective: \"REPLACE\",\n }),\n ...(tags && {\n Tagging: this._tagsToQuery(tags),\n TaggingDirective: \"REPLACE\",\n }),\n });\n return this.client.send(cmd);\n }\n\n // ── LIST ────────────────────────────────────────────────────────────────\n async listObjects(prefix, { keysOnly = false, maxKeys = 1000, continuationToken } = {}) {\n const out = await this.client.send(new ListObjectsV2Command({\n Bucket: this.bucketName,\n Prefix: prefix,\n MaxKeys: maxKeys,\n ContinuationToken: continuationToken,\n }));\n const items = (out.Contents ?? []).map(o => ({\n key: o.Key,\n size: o.Size,\n etag: o.ETag,\n lastModified: o.LastModified,\n storageClass: o.StorageClass,\n }));\n return {\n items: keysOnly ? items.map(i => i.key) : items,\n isTruncated: !!out.IsTruncated,\n nextContinuationToken: out.NextContinuationToken,\n };\n }\n\n // ── TAGS (used for lifecycle rules, e.g. status=closed → Glacier IR) ────\n async putObjectTagging(key, tags) {\n return this.client.send(new PutObjectTaggingCommand({\n Bucket: this.bucketName,\n Key: key,\n Tagging: { TagSet: this._tagsToTagSet(tags) },\n }));\n }\n\n async getObjectTagging(key) {\n const out = await this.client.send(new GetObjectTaggingCommand({\n Bucket: this.bucketName,\n Key: key,\n }));\n const tags = {};\n for (const t of out.TagSet ?? []) tags[t.Key] = t.Value;\n return tags;\n }\n\n // ── internals ───────────────────────────────────────────────────────────\n _isNotFound(err) {\n const status = err?.$metadata?.httpStatusCode;\n return status === 404\n || err?.name === \"NotFound\"\n || err?.name === \"NoSuchKey\"\n || err?.Code === \"NoSuchKey\";\n }\n\n _tagsToQuery(tags) {\n return Object.entries(tags)\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)\n .join(\"&\");\n }\n\n _tagsToTagSet(tags) {\n return Object.entries(tags).map(([Key, Value]) => ({ Key, Value: String(Value) }));\n }\n}\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\nexport class HttpClientError extends FrameworkError {\n constructor(message, cause) {\n super(message);this.cause = cause;;\n this.name = \"HttpClientError\";\n }\n}\n\nexport class FileDatabaseError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"FileDatabaseError\";\n }\n}\n\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nmakarov/cli-toolkit",
3
- "version": "0.46.0",
3
+ "version": "0.47.0",
4
4
  "description": "A comprehensive toolkit for building CLI applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",