@stacksjs/ts-cloud 0.2.22 → 0.2.23
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/aws/s3.d.ts +17 -0
- package/dist/bin/cli.js +590 -549
- package/dist/index.d.ts +2 -0
- package/dist/index.js +223 -0
- package/dist/object-storage/index.d.ts +1 -0
- package/dist/object-storage/migrate.d.ts +125 -0
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export { AWSClient, CloudFormationClient, CloudFormationClient as AWSCloudFormat
|
|
|
6
6
|
export type { AWSRequestOptions, AWSClientConfig, AWSError, AWSCredentials as AWSClientCredentials, StackParameter, StackTag, CreateStackOptions, UpdateStackOptions, DescribeStacksOptions, StackEvent, Stack, InvalidationOptions, Distribution, S3SyncOptions, S3CopyOptions, S3ListOptions, S3Object, CertificateDetail, Certificate as ELBv2Certificate, RekognitionS3Object, RekognitionBoundingBox, TextractS3Object, TextractBoundingBox, CountryCode, ContactType, ContactDetail, KendraCreateDataSourceCommandInput, KendraCreateDataSourceCommandOutput, KendraListDataSourcesCommandInput, KendraListDataSourcesCommandOutput, InvokeModelCommandInput, InvokeModelCommandOutput, InvokeModelWithResponseStreamCommandInput, InvokeModelWithResponseStreamCommandOutput, CreateModelCustomizationJobCommandInput, CreateModelCustomizationJobCommandOutput, GetModelCustomizationJobCommandInput, GetModelCustomizationJobCommandOutput, ListFoundationModelsCommandInput, ListFoundationModelsCommandOutput, AttributeValue as DynamoDBAttributeValue, KeySchemaElement, AttributeDefinition as DynamoDBAttributeDefinition, } from './aws';
|
|
7
7
|
export { createObjectStorageClient, providerEndpoint, resolveObjectStorage, } from './object-storage';
|
|
8
8
|
export type { ObjectStorageConfig, ObjectStorageCredentials, ObjectStorageProvider, ResolvedObjectStorage, } from './object-storage';
|
|
9
|
+
export { keyMatchesFilters, migrateObjectStorage, remapKey, } from './object-storage/migrate';
|
|
10
|
+
export type { MigrateEndpoint, MigrateError, MigrateOptions, MigratePlanItem, MigrateProgress, MigrateResult, MigrateVerification, } from './object-storage/migrate';
|
|
9
11
|
export * from './ssl';
|
|
10
12
|
export { deployStaticSite, deployStaticSiteFull, uploadStaticFiles, invalidateCache, deleteStaticSite, generateStaticSiteTemplate, deployStaticSiteWithExternalDns, deployStaticSiteWithExternalDnsFull, generateExternalDnsStaticSiteTemplate, deploySite, resolveSiteDeployTarget, resolveSiteKind, validateDeploymentConfig, } from './deploy';
|
|
11
13
|
export type { StaticSiteConfig, DeployResult, UploadOptions, ExternalDnsStaticSiteConfig, ExternalDnsDeployResult, DeploySiteConfig, DeploySiteResult, StaticSiteDnsProvider, SiteDeployKind, DeploymentValidationResult, } from './deploy';
|
package/dist/index.js
CHANGED
|
@@ -2123,6 +2123,67 @@ class S3Client2 {
|
|
|
2123
2123
|
});
|
|
2124
2124
|
return result;
|
|
2125
2125
|
}
|
|
2126
|
+
async getObjectBytes(bucket, key) {
|
|
2127
|
+
const { accessKeyId, secretAccessKey, sessionToken } = this.getCredentials();
|
|
2128
|
+
const host = this.s3VirtualHost(bucket);
|
|
2129
|
+
const encodedKey = key.split("/").map((seg) => encodeURIComponent(seg)).join("/");
|
|
2130
|
+
const canonicalUri = this.forcePathStyle ? `/${bucket}/${encodedKey}` : `/${encodedKey}`;
|
|
2131
|
+
const url = `https://${host}${canonicalUri}`;
|
|
2132
|
+
const now = new Date;
|
|
2133
|
+
const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, "");
|
|
2134
|
+
const dateStamp = now.toISOString().slice(0, 10).replace(/-/g, "");
|
|
2135
|
+
const payloadHash = crypto3.createHash("sha256").update("").digest("hex");
|
|
2136
|
+
const requestHeaders = {
|
|
2137
|
+
host,
|
|
2138
|
+
"x-amz-date": amzDate,
|
|
2139
|
+
"x-amz-content-sha256": payloadHash
|
|
2140
|
+
};
|
|
2141
|
+
if (sessionToken) {
|
|
2142
|
+
requestHeaders["x-amz-security-token"] = sessionToken;
|
|
2143
|
+
}
|
|
2144
|
+
const canonicalHeaders = Object.keys(requestHeaders).sort().map((k) => `${k.toLowerCase()}:${requestHeaders[k].trim()}
|
|
2145
|
+
`).join("");
|
|
2146
|
+
const signedHeaders = Object.keys(requestHeaders).sort().map((k) => k.toLowerCase()).join(";");
|
|
2147
|
+
const canonicalRequest = [
|
|
2148
|
+
"GET",
|
|
2149
|
+
canonicalUri,
|
|
2150
|
+
"",
|
|
2151
|
+
canonicalHeaders,
|
|
2152
|
+
signedHeaders,
|
|
2153
|
+
payloadHash
|
|
2154
|
+
].join(`
|
|
2155
|
+
`);
|
|
2156
|
+
const algorithm = "AWS4-HMAC-SHA256";
|
|
2157
|
+
const credentialScope = `${dateStamp}/${this.region}/s3/aws4_request`;
|
|
2158
|
+
const stringToSign = [
|
|
2159
|
+
algorithm,
|
|
2160
|
+
amzDate,
|
|
2161
|
+
credentialScope,
|
|
2162
|
+
crypto3.createHash("sha256").update(canonicalRequest).digest("hex")
|
|
2163
|
+
].join(`
|
|
2164
|
+
`);
|
|
2165
|
+
const kDate = crypto3.createHmac("sha256", `AWS4${secretAccessKey}`).update(dateStamp).digest();
|
|
2166
|
+
const kRegion = crypto3.createHmac("sha256", kDate).update(this.region).digest();
|
|
2167
|
+
const kService = crypto3.createHmac("sha256", kRegion).update("s3").digest();
|
|
2168
|
+
const kSigning = crypto3.createHmac("sha256", kService).update("aws4_request").digest();
|
|
2169
|
+
const signature = crypto3.createHmac("sha256", kSigning).update(stringToSign).digest("hex");
|
|
2170
|
+
const authorizationHeader = `${algorithm} Credential=${accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
2171
|
+
const response = await fetch(url, {
|
|
2172
|
+
method: "GET",
|
|
2173
|
+
headers: { ...requestHeaders, Authorization: authorizationHeader }
|
|
2174
|
+
});
|
|
2175
|
+
if (!response.ok) {
|
|
2176
|
+
const errorText = await response.text();
|
|
2177
|
+
throw new Error(`S3 GET failed: ${response.status} ${errorText}`);
|
|
2178
|
+
}
|
|
2179
|
+
const buffer = await response.arrayBuffer();
|
|
2180
|
+
const contentLengthHeader = response.headers.get("content-length");
|
|
2181
|
+
return {
|
|
2182
|
+
body: new Uint8Array(buffer),
|
|
2183
|
+
contentType: response.headers.get("content-type") ?? undefined,
|
|
2184
|
+
contentLength: contentLengthHeader ? Number.parseInt(contentLengthHeader, 10) : undefined
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2126
2187
|
async copyObject(options) {
|
|
2127
2188
|
const headers = {
|
|
2128
2189
|
"x-amz-copy-source": `/${options.sourceBucket}/${options.sourceKey}`
|
|
@@ -65570,6 +65631,165 @@ init_client();
|
|
|
65570
65631
|
|
|
65571
65632
|
// src/object-storage/index.ts
|
|
65572
65633
|
init_s3();
|
|
65634
|
+
|
|
65635
|
+
// src/object-storage/migrate.ts
|
|
65636
|
+
function stripPrefix(key, prefix) {
|
|
65637
|
+
if (!prefix)
|
|
65638
|
+
return key;
|
|
65639
|
+
return key.startsWith(prefix) ? key.slice(prefix.length) : key;
|
|
65640
|
+
}
|
|
65641
|
+
function remapKey(sourceKey, fromPrefix, toPrefix) {
|
|
65642
|
+
const stripped = stripPrefix(sourceKey, fromPrefix);
|
|
65643
|
+
return `${toPrefix ?? ""}${stripped}`;
|
|
65644
|
+
}
|
|
65645
|
+
function keyMatchesFilters(key, include, exclude) {
|
|
65646
|
+
if (exclude && exclude.some((p) => key.startsWith(p)))
|
|
65647
|
+
return false;
|
|
65648
|
+
if (include && include.length > 0)
|
|
65649
|
+
return include.some((p) => key.startsWith(p));
|
|
65650
|
+
return true;
|
|
65651
|
+
}
|
|
65652
|
+
async function mapWithConcurrency(items, limit, worker) {
|
|
65653
|
+
let cursor = 0;
|
|
65654
|
+
const runners = [];
|
|
65655
|
+
const size = Math.max(1, Math.min(limit, items.length || 1));
|
|
65656
|
+
for (let i = 0;i < size; i++) {
|
|
65657
|
+
runners.push((async () => {
|
|
65658
|
+
while (true) {
|
|
65659
|
+
const index = cursor++;
|
|
65660
|
+
if (index >= items.length)
|
|
65661
|
+
return;
|
|
65662
|
+
await worker(items[index], index);
|
|
65663
|
+
}
|
|
65664
|
+
})());
|
|
65665
|
+
}
|
|
65666
|
+
await Promise.all(runners);
|
|
65667
|
+
}
|
|
65668
|
+
function clientFor(endpoint) {
|
|
65669
|
+
if (endpoint.client)
|
|
65670
|
+
return endpoint.client;
|
|
65671
|
+
return createObjectStorageClient({
|
|
65672
|
+
provider: endpoint.provider,
|
|
65673
|
+
region: endpoint.region,
|
|
65674
|
+
endpoint: endpoint.endpoint,
|
|
65675
|
+
forcePathStyle: endpoint.forcePathStyle,
|
|
65676
|
+
credentials: endpoint.credentials
|
|
65677
|
+
});
|
|
65678
|
+
}
|
|
65679
|
+
async function migrateObjectStorage(options) {
|
|
65680
|
+
const concurrency = options.concurrency ?? 8;
|
|
65681
|
+
const fromClient = clientFor(options.from);
|
|
65682
|
+
const toClient = clientFor(options.to);
|
|
65683
|
+
const sourceObjects = await fromClient.listAllObjects({
|
|
65684
|
+
bucket: options.from.bucket,
|
|
65685
|
+
prefix: options.from.prefix
|
|
65686
|
+
});
|
|
65687
|
+
const result = {
|
|
65688
|
+
copied: 0,
|
|
65689
|
+
skipped: 0,
|
|
65690
|
+
excluded: 0,
|
|
65691
|
+
bytesCopied: 0,
|
|
65692
|
+
errors: [],
|
|
65693
|
+
excludedKeys: [],
|
|
65694
|
+
deleted: []
|
|
65695
|
+
};
|
|
65696
|
+
const toCopy = [];
|
|
65697
|
+
for (const obj of sourceObjects) {
|
|
65698
|
+
if (!keyMatchesFilters(obj.Key, options.include, options.exclude)) {
|
|
65699
|
+
result.excluded++;
|
|
65700
|
+
result.excludedKeys.push(obj.Key);
|
|
65701
|
+
continue;
|
|
65702
|
+
}
|
|
65703
|
+
toCopy.push({ source: obj, destKey: remapKey(obj.Key, options.from.prefix, options.to.prefix) });
|
|
65704
|
+
}
|
|
65705
|
+
if (options.dryRun) {
|
|
65706
|
+
result.plan = toCopy.map(({ source, destKey }) => ({ key: source.Key, destKey, size: source.Size }));
|
|
65707
|
+
const total2 = sourceObjects.length;
|
|
65708
|
+
let index = 0;
|
|
65709
|
+
for (const obj of sourceObjects) {
|
|
65710
|
+
index++;
|
|
65711
|
+
const excluded = !keyMatchesFilters(obj.Key, options.include, options.exclude);
|
|
65712
|
+
options.onProgress?.({
|
|
65713
|
+
key: obj.Key,
|
|
65714
|
+
destKey: excluded ? "" : remapKey(obj.Key, options.from.prefix, options.to.prefix),
|
|
65715
|
+
size: obj.Size,
|
|
65716
|
+
action: excluded ? "excluded" : "planned",
|
|
65717
|
+
index,
|
|
65718
|
+
total: total2
|
|
65719
|
+
});
|
|
65720
|
+
}
|
|
65721
|
+
return result;
|
|
65722
|
+
}
|
|
65723
|
+
const total = toCopy.length;
|
|
65724
|
+
let processed = 0;
|
|
65725
|
+
await mapWithConcurrency(toCopy, concurrency, async ({ source, destKey }) => {
|
|
65726
|
+
const myIndex = ++processed;
|
|
65727
|
+
try {
|
|
65728
|
+
if (!options.force) {
|
|
65729
|
+
const head = await toClient.headObject(options.to.bucket, destKey);
|
|
65730
|
+
if (head && head.ContentLength === source.Size) {
|
|
65731
|
+
result.skipped++;
|
|
65732
|
+
options.onProgress?.({ key: source.Key, destKey, size: source.Size, action: "skipped", index: myIndex, total });
|
|
65733
|
+
return;
|
|
65734
|
+
}
|
|
65735
|
+
}
|
|
65736
|
+
const { body, contentType } = await fromClient.getObjectBytes(options.from.bucket, source.Key);
|
|
65737
|
+
await toClient.putObject({
|
|
65738
|
+
bucket: options.to.bucket,
|
|
65739
|
+
key: destKey,
|
|
65740
|
+
body,
|
|
65741
|
+
contentType
|
|
65742
|
+
});
|
|
65743
|
+
result.copied++;
|
|
65744
|
+
result.bytesCopied += body.byteLength;
|
|
65745
|
+
options.onProgress?.({ key: source.Key, destKey, size: source.Size, action: "copied", index: myIndex, total });
|
|
65746
|
+
} catch (err) {
|
|
65747
|
+
result.errors.push({ key: source.Key, message: err?.message ?? String(err) });
|
|
65748
|
+
options.onProgress?.({ key: source.Key, destKey, size: source.Size, action: "error", index: myIndex, total });
|
|
65749
|
+
}
|
|
65750
|
+
});
|
|
65751
|
+
const copiedDestKeys = new Set(toCopy.map((c) => c.destKey));
|
|
65752
|
+
if (options.deleteExtraneous) {
|
|
65753
|
+
const destObjects = await toClient.listAllObjects({ bucket: options.to.bucket, prefix: options.to.prefix });
|
|
65754
|
+
const extraneous = destObjects.filter((o) => !copiedDestKeys.has(o.Key)).map((o) => o.Key);
|
|
65755
|
+
for (const key of extraneous) {
|
|
65756
|
+
try {
|
|
65757
|
+
await toClient.deleteObject(options.to.bucket, key);
|
|
65758
|
+
result.deleted.push(key);
|
|
65759
|
+
} catch (err) {
|
|
65760
|
+
result.errors.push({ key, message: `delete failed: ${err?.message ?? String(err)}` });
|
|
65761
|
+
}
|
|
65762
|
+
}
|
|
65763
|
+
}
|
|
65764
|
+
if (options.verify) {
|
|
65765
|
+
const destObjects = await toClient.listAllObjects({ bucket: options.to.bucket, prefix: options.to.prefix });
|
|
65766
|
+
const destBySizeKey = new Map(destObjects.map((o) => [o.Key, o.Size]));
|
|
65767
|
+
const missing = [];
|
|
65768
|
+
const sizeMismatches = [];
|
|
65769
|
+
let matched = 0;
|
|
65770
|
+
for (const { source, destKey } of toCopy) {
|
|
65771
|
+
if (!destBySizeKey.has(destKey)) {
|
|
65772
|
+
missing.push(destKey);
|
|
65773
|
+
continue;
|
|
65774
|
+
}
|
|
65775
|
+
const actual = destBySizeKey.get(destKey);
|
|
65776
|
+
if (actual !== source.Size) {
|
|
65777
|
+
sizeMismatches.push({ key: destKey, expected: source.Size, actual });
|
|
65778
|
+
continue;
|
|
65779
|
+
}
|
|
65780
|
+
matched++;
|
|
65781
|
+
}
|
|
65782
|
+
result.verification = {
|
|
65783
|
+
ok: missing.length === 0 && sizeMismatches.length === 0,
|
|
65784
|
+
matched,
|
|
65785
|
+
missing,
|
|
65786
|
+
sizeMismatches
|
|
65787
|
+
};
|
|
65788
|
+
}
|
|
65789
|
+
return result;
|
|
65790
|
+
}
|
|
65791
|
+
|
|
65792
|
+
// src/object-storage/index.ts
|
|
65573
65793
|
var DEFAULT_REGION = {
|
|
65574
65794
|
aws: "us-east-1",
|
|
65575
65795
|
backblaze: "us-west-004",
|
|
@@ -82396,6 +82616,7 @@ export {
|
|
|
82396
82616
|
resolveCaddyfile,
|
|
82397
82617
|
requiresReplacement,
|
|
82398
82618
|
replicaManager,
|
|
82619
|
+
remapKey,
|
|
82399
82620
|
regionPairManager,
|
|
82400
82621
|
quickHash,
|
|
82401
82622
|
queueManagementManager,
|
|
@@ -82417,6 +82638,7 @@ export {
|
|
|
82417
82638
|
multiRegionManager,
|
|
82418
82639
|
multiAccountManager,
|
|
82419
82640
|
migrationManager,
|
|
82641
|
+
migrateObjectStorage,
|
|
82420
82642
|
metricsManager,
|
|
82421
82643
|
mergeInfrastructure,
|
|
82422
82644
|
makeAWSRequestOnce,
|
|
@@ -82430,6 +82652,7 @@ export {
|
|
|
82430
82652
|
lambdaDestinationsManager,
|
|
82431
82653
|
lambdaDLQManager,
|
|
82432
82654
|
lambdaConcurrencyManager,
|
|
82655
|
+
keyMatchesFilters,
|
|
82433
82656
|
isWebCryptoAvailable,
|
|
82434
82657
|
isValidRegion,
|
|
82435
82658
|
isOnDemandDomain,
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-provider object-storage migration.
|
|
3
|
+
*
|
|
4
|
+
* Copies objects from one S3-compatible bucket to another — AWS S3, Backblaze
|
|
5
|
+
* B2, Hetzner Object Storage, in any direction. Both sides are driven by the
|
|
6
|
+
* same {@link createObjectStorageClient}, so the only thing that changes per
|
|
7
|
+
* side is the provider/region/endpoint/credentials.
|
|
8
|
+
*
|
|
9
|
+
* Bytes are copied (not strings) via {@link S3Client.getObjectBytes}, so binary
|
|
10
|
+
* payloads (images, archives, mail attachments) survive intact. Content-Type is
|
|
11
|
+
* preserved when the source reports it.
|
|
12
|
+
*
|
|
13
|
+
* The copy is idempotent: an object already present at the destination with the
|
|
14
|
+
* same size is skipped unless `force` is set. Keys may be remapped by stripping
|
|
15
|
+
* `fromPrefix` and prepending `toPrefix`, and filtered with include/exclude
|
|
16
|
+
* prefix lists so an operator can clearly see what was migrated vs. deliberately
|
|
17
|
+
* left behind.
|
|
18
|
+
*/
|
|
19
|
+
import type { S3Client } from '../aws/s3';
|
|
20
|
+
import type { ObjectStorageProvider } from './index';
|
|
21
|
+
/** One side (source or destination) of a migration. */
|
|
22
|
+
export interface MigrateEndpoint {
|
|
23
|
+
provider: ObjectStorageProvider;
|
|
24
|
+
bucket: string;
|
|
25
|
+
region?: string;
|
|
26
|
+
/** Endpoint host override (no scheme). Defaults to the provider's standard endpoint. */
|
|
27
|
+
endpoint?: string;
|
|
28
|
+
forcePathStyle?: boolean;
|
|
29
|
+
/** Key prefix. On the source it scopes/strips; on the dest it is prepended. */
|
|
30
|
+
prefix?: string;
|
|
31
|
+
/** Explicit credentials. When omitted, resolved from the provider's env vars. */
|
|
32
|
+
credentials?: {
|
|
33
|
+
accessKeyId: string;
|
|
34
|
+
secretAccessKey: string;
|
|
35
|
+
sessionToken?: string;
|
|
36
|
+
};
|
|
37
|
+
/** Pre-built client (used by tests to inject an in-memory mock). */
|
|
38
|
+
client?: S3Client;
|
|
39
|
+
}
|
|
40
|
+
export interface MigrateOptions {
|
|
41
|
+
from: MigrateEndpoint;
|
|
42
|
+
to: MigrateEndpoint;
|
|
43
|
+
/** Only copy keys whose (source) key starts with one of these prefixes. */
|
|
44
|
+
include?: string[];
|
|
45
|
+
/** Skip keys whose (source) key starts with one of these prefixes. */
|
|
46
|
+
exclude?: string[];
|
|
47
|
+
/** Plan only — do not write to the destination. */
|
|
48
|
+
dryRun?: boolean;
|
|
49
|
+
/** Re-copy even if the destination already has an object of the same size. */
|
|
50
|
+
force?: boolean;
|
|
51
|
+
/** Delete destination keys (within the dest prefix) that are not in the copied set. Default OFF. */
|
|
52
|
+
deleteExtraneous?: boolean;
|
|
53
|
+
/** Max in-flight copies. Default 8. */
|
|
54
|
+
concurrency?: number;
|
|
55
|
+
/** After copying, re-list the destination and assert counts + sizes match the copied set. */
|
|
56
|
+
verify?: boolean;
|
|
57
|
+
/** Optional progress callback, invoked per object after it is copied/skipped. */
|
|
58
|
+
onProgress?: (event: MigrateProgress) => void;
|
|
59
|
+
}
|
|
60
|
+
export interface MigrateProgress {
|
|
61
|
+
/** Source key. */
|
|
62
|
+
key: string;
|
|
63
|
+
/** Destination key the object maps to. */
|
|
64
|
+
destKey: string;
|
|
65
|
+
size: number;
|
|
66
|
+
action: 'copied' | 'skipped' | 'excluded' | 'error' | 'planned';
|
|
67
|
+
/** 1-based index of this object within the full source listing. */
|
|
68
|
+
index: number;
|
|
69
|
+
total: number;
|
|
70
|
+
}
|
|
71
|
+
export interface MigrateError {
|
|
72
|
+
key: string;
|
|
73
|
+
message: string;
|
|
74
|
+
}
|
|
75
|
+
export interface MigratePlanItem {
|
|
76
|
+
key: string;
|
|
77
|
+
destKey: string;
|
|
78
|
+
size: number;
|
|
79
|
+
}
|
|
80
|
+
export interface MigrateResult {
|
|
81
|
+
copied: number;
|
|
82
|
+
skipped: number;
|
|
83
|
+
excluded: number;
|
|
84
|
+
bytesCopied: number;
|
|
85
|
+
errors: MigrateError[];
|
|
86
|
+
/** Keys that were excluded by include/exclude filters (source keys). */
|
|
87
|
+
excludedKeys: string[];
|
|
88
|
+
/** Keys deleted from the destination via `deleteExtraneous`. */
|
|
89
|
+
deleted: string[];
|
|
90
|
+
/** When `dryRun` is set, the objects that would be copied. */
|
|
91
|
+
plan?: MigratePlanItem[];
|
|
92
|
+
/** Verification outcome when `verify` is set. */
|
|
93
|
+
verification?: MigrateVerification;
|
|
94
|
+
}
|
|
95
|
+
export interface MigrateVerification {
|
|
96
|
+
ok: boolean;
|
|
97
|
+
/** Number of (key,size) pairs that matched at the destination. */
|
|
98
|
+
matched: number;
|
|
99
|
+
/** Copied keys missing at the destination. */
|
|
100
|
+
missing: string[];
|
|
101
|
+
/** Copied keys present at the destination but with a different size. */
|
|
102
|
+
sizeMismatches: Array<{
|
|
103
|
+
key: string;
|
|
104
|
+
expected: number;
|
|
105
|
+
actual: number;
|
|
106
|
+
}>;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Compute the destination key for a source key: strip the source prefix, then
|
|
110
|
+
* prepend the destination prefix. Exported for unit testing.
|
|
111
|
+
*/
|
|
112
|
+
export declare function remapKey(sourceKey: string, fromPrefix?: string, toPrefix?: string): string;
|
|
113
|
+
/**
|
|
114
|
+
* Decide whether a source key passes the include/exclude prefix filters.
|
|
115
|
+
* `include` (when non-empty) is a whitelist; `exclude` always wins. Exported for
|
|
116
|
+
* unit testing.
|
|
117
|
+
*/
|
|
118
|
+
export declare function keyMatchesFilters(key: string, include?: string[], exclude?: string[]): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Migrate objects from one S3-compatible bucket to another.
|
|
121
|
+
*
|
|
122
|
+
* @returns a structured {@link MigrateResult} so callers (CLI, buddy, scripts)
|
|
123
|
+
* can report exactly what was copied, skipped, excluded, deleted and verified.
|
|
124
|
+
*/
|
|
125
|
+
export declare function migrateObjectStorage(options: MigrateOptions): Promise<MigrateResult>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/ts-cloud",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.23",
|
|
5
5
|
"description": "A lightweight, performant infrastructure-as-code library and CLI for deploying both server-based (EC2) and serverless applications.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
"test": "bun test"
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {
|
|
92
|
-
"@ts-cloud/aws-types": "0.2.
|
|
93
|
-
"@ts-cloud/core": "0.2.
|
|
92
|
+
"@ts-cloud/aws-types": "0.2.23",
|
|
93
|
+
"@ts-cloud/core": "0.2.23",
|
|
94
94
|
"@stacksjs/ts-xml": "^0.1.0"
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|