qstd 0.3.53 → 0.3.55

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.
@@ -0,0 +1,51 @@
1
+ import { type DynamoDBClientConfig } from "@aws-sdk/client-dynamodb";
2
+ type CopyTableProps = {
3
+ /** Source table configuration */
4
+ source: {
5
+ tableName: string;
6
+ /** Optional AWS config for cross-region copying */
7
+ config?: DynamoDBClientConfig;
8
+ };
9
+ /** Destination table configuration */
10
+ destination: {
11
+ tableName: string;
12
+ /** Optional AWS config for cross-region copying */
13
+ config?: DynamoDBClientConfig;
14
+ };
15
+ /** Whether to create destination table if it doesn't exist */
16
+ create?: boolean;
17
+ /** If true, only copy schema without data */
18
+ schemaOnly?: boolean;
19
+ /** Whether to enable continuous backups on destination table */
20
+ continuousBackups?: boolean;
21
+ /** Optional function to transform each item during copy */
22
+ transform?: (item: Record<string, unknown>, index: number) => Record<string, unknown>;
23
+ /** Whether to show progress logs */
24
+ log?: boolean;
25
+ };
26
+ type CopyTableResult = {
27
+ /** Number of items copied */
28
+ count: number;
29
+ /** Operation status */
30
+ status: "SUCCESS" | "FAIL";
31
+ /** Whether only schema was copied */
32
+ schemaOnly?: boolean;
33
+ };
34
+ /**
35
+ * Copy a DynamoDB table to another table, with support for cross-region copying,
36
+ * schema-only copying, and data transformation.
37
+ *
38
+ * This function can:
39
+ * - Copy data between tables in the same or different regions
40
+ * - Create the destination table if it doesn't exist
41
+ * - Copy only the schema without data
42
+ * - Transform items during the copy process
43
+ * - Enable continuous backups on the destination table
44
+ * - Handle large tables with proper pagination and retry logic
45
+ *
46
+ * @param props Configuration options for the copy operation
47
+ * @returns Promise resolving to copy results
48
+ */
49
+ export declare const copyTable: (props: CopyTableProps) => Promise<CopyTableResult>;
50
+ export {};
51
+ //# sourceMappingURL=copy-table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-table.d.ts","sourceRoot":"","sources":["../../../../src/server/aws/ddb/copy-table.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,oBAAoB,EAG1B,MAAM,0BAA0B,CAAC;AAqKlC,KAAK,cAAc,GAAG;IACpB,iCAAiC;IACjC,MAAM,EAAE;QACN,SAAS,EAAE,MAAM,CAAC;QAClB,mDAAmD;QACnD,MAAM,CAAC,EAAE,oBAAoB,CAAC;KAC/B,CAAC;IACF,sCAAsC;IACtC,WAAW,EAAE;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,mDAAmD;QACnD,MAAM,CAAC,EAAE,oBAAoB,CAAC;KAC/B,CAAC;IACF,8DAA8D;IAC9D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,2DAA2D;IAC3D,SAAS,CAAC,EAAE,CACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,MAAM,KACV,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,oCAAoC;IACpC,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,MAAM,EAAE,SAAS,GAAG,MAAM,CAAC;IAC3B,qCAAqC;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,GACpB,OAAO,cAAc,KACpB,OAAO,CAAC,eAAe,CAsMzB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export * from "./domain";
2
2
  export * from "./literals";
3
+ export { copyTable } from "./copy-table";
3
4
  export type { Key } from "./types";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/server/aws/ddb/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,YAAY,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/server/aws/ddb/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC"}
@@ -1,4 +1,4 @@
1
- import { type CreateBucketCommandOutput, type DeleteBucketCommandOutput, type DeleteObjectCommandOutput, type GetObjectCommandOutput, type HeadObjectCommandOutput } from "@aws-sdk/client-s3";
1
+ import { type DeleteObjectCommandOutput, type GetObjectCommandOutput, type HeadObjectCommandOutput } from "@aws-sdk/client-s3";
2
2
  import { type PresignedPost } from "@aws-sdk/s3-presigned-post";
3
3
  import * as _t from "./types";
4
4
  export declare const create: (props?: _t.CreateProps) => _t.Client;
@@ -25,19 +25,18 @@ export type FileProps = {
25
25
  export declare const getFile: (s3: _t.Client, props: FileProps) => Promise<GetObjectCommandOutput>;
26
26
  export declare const deleteFile: (s3: _t.Client, props: FileProps) => Promise<DeleteObjectCommandOutput>;
27
27
  export declare const getFileMetadata: (s3: _t.Client, props: FileProps) => Promise<HeadObjectCommandOutput>;
28
- /** create a new bucket */
29
- export declare function bucketHandle(s3: _t.Client, props: {
30
- action: "create";
31
- bucketName: string;
32
- }): Promise<CreateBucketCommandOutput>;
33
- export declare function bucketHandle(s3: _t.Client, props: {
34
- action: "delete";
35
- bucketName: string;
36
- }): Promise<DeleteBucketCommandOutput>;
37
- export declare function bucketHandle(s3: _t.Client, props: {
38
- action: "exists";
39
- bucketName: string;
40
- }): Promise<boolean>;
28
+ /**
29
+ * Create a new S3 bucket
30
+ */
31
+ export declare const createBucket: (s3: _t.Client, bucketName: string) => Promise<import("@aws-sdk/client-s3").CreateBucketCommandOutput>;
32
+ /**
33
+ * Delete an S3 bucket
34
+ */
35
+ export declare const deleteBucket: (s3: _t.Client, bucketName: string) => Promise<import("@aws-sdk/client-s3").DeleteBucketCommandOutput>;
36
+ /**
37
+ * Check if an S3 bucket exists
38
+ */
39
+ export declare const bucketExists: (s3: _t.Client, bucketName: string) => Promise<boolean>;
41
40
  /**
42
41
  * Copy and delete original files from src bucket to target bucket.
43
42
  * @param s3
@@ -1 +1 @@
1
- {"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../../../src/server/aws/s3/domain.ts"],"names":[],"mappings":"AAAA,OAAO,EAYL,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC9B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAE7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,eAAO,MAAM,MAAM,GAAI,QAAO,EAAE,CAAC,WAAgB,KAAG,EAAE,CAAC,MAKtD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,EAAE,CAAC,MAAM,EACb,KAAK,EAAE,EAAE,CAAC,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAAC;AACnB,wBAAgB,eAAe,CAC7B,EAAE,EAAE,EAAE,CAAC,MAAM,EACb,KAAK,EAAE,EAAE,CAAC,kBAAkB,GAC3B,OAAO,CAAC,aAAa,CAAC,CAAC;AA4C1B;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,WAAW,iEAU9D,CAAC;AAMF,MAAM,MAAM,SAAS,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,eAAO,MAAM,OAAO,GAClB,IAAI,EAAE,CAAC,MAAM,EACb,OAAO,SAAS,KACf,OAAO,CAAC,sBAAsB,CAWhC,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,IAAI,EAAE,CAAC,MAAM,EACb,OAAO,SAAS,KACf,OAAO,CAAC,yBAAyB,CAWnC,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,IAAI,EAAE,CAAC,MAAM,EACb,OAAO,SAAS,KACf,OAAO,CAAC,uBAAuB,CAWjC,CAAC;AAMF,0BAA0B;AAC1B,wBAAgB,YAAY,CAC1B,EAAE,EAAE,EAAE,CAAC,MAAM,EACb,KAAK,EAAE;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC9C,OAAO,CAAC,yBAAyB,CAAC,CAAC;AACtC,wBAAgB,YAAY,CAC1B,EAAE,EAAE,EAAE,CAAC,MAAM,EACb,KAAK,EAAE;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC9C,OAAO,CAAC,yBAAyB,CAAC,CAAC;AACtC,wBAAgB,YAAY,CAC1B,EAAE,EAAE,EAAE,CAAC,MAAM,EACb,KAAK,EAAE;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC9C,OAAO,CAAC,OAAO,CAAC,CAAC;AAmCpB;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAChC,IAAI,EAAE,CAAC,MAAM,EACb,SAAS,EAAE,CAAC,kBAAkB,qDAkB/B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,EAAE,CAAC,SAAS,EAAE,GAAG,IAgB9D,CAAC"}
1
+ {"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../../../src/server/aws/s3/domain.ts"],"names":[],"mappings":"AAAA,OAAO,EAYL,KAAK,yBAAyB,EAC9B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAE7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,eAAO,MAAM,MAAM,GAAI,QAAO,EAAE,CAAC,WAAgB,KAAG,EAAE,CAAC,MAKtD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,EAAE,CAAC,MAAM,EACb,KAAK,EAAE,EAAE,CAAC,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAAC;AACnB,wBAAgB,eAAe,CAC7B,EAAE,EAAE,EAAE,CAAC,MAAM,EACb,KAAK,EAAE,EAAE,CAAC,kBAAkB,GAC3B,OAAO,CAAC,aAAa,CAAC,CAAC;AA4C1B;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,WAAW,iEAU9D,CAAC;AAMF,MAAM,MAAM,SAAS,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,eAAO,MAAM,OAAO,GAClB,IAAI,EAAE,CAAC,MAAM,EACb,OAAO,SAAS,KACf,OAAO,CAAC,sBAAsB,CAWhC,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,IAAI,EAAE,CAAC,MAAM,EACb,OAAO,SAAS,KACf,OAAO,CAAC,yBAAyB,CAWnC,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,IAAI,EAAE,CAAC,MAAM,EACb,OAAO,SAAS,KACf,OAAO,CAAC,uBAAuB,CAWjC,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,IAAI,EAAE,CAAC,MAAM,EAAE,YAAY,MAAM,oEAG7D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,IAAI,EAAE,CAAC,MAAM,EAAE,YAAY,MAAM,oEAG7D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,GAAU,IAAI,EAAE,CAAC,MAAM,EAAE,YAAY,MAAM,qBAgBnE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAChC,IAAI,EAAE,CAAC,MAAM,EACb,SAAS,EAAE,CAAC,kBAAkB,qDAkB/B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,EAAE,CAAC,SAAS,EAAE,GAAG,IAgB9D,CAAC"}
@@ -6,6 +6,7 @@ var fs = require('fs');
6
6
  var arktype = require('arktype');
7
7
  var libDynamodb = require('@aws-sdk/lib-dynamodb');
8
8
  var clientDynamodb = require('@aws-sdk/client-dynamodb');
9
+ var signale = require('signale');
9
10
  var clientSns = require('@aws-sdk/client-sns');
10
11
  var clientSqs = require('@aws-sdk/client-sqs');
11
12
  var clientSes = require('@aws-sdk/client-ses');
@@ -17,6 +18,7 @@ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentS
17
18
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
19
 
19
20
  var awaitSpawn__default = /*#__PURE__*/_interopDefault(awaitSpawn);
21
+ var signale__default = /*#__PURE__*/_interopDefault(signale);
20
22
 
21
23
  var __defProp = Object.defineProperty;
22
24
  var __export = (target, all) => {
@@ -1060,6 +1062,7 @@ __export(ddb_exports, {
1060
1062
  batchDelete: () => batchDelete,
1061
1063
  batchGet: () => batchGet,
1062
1064
  batchWrite: () => batchWrite,
1065
+ copyTable: () => copyTable,
1063
1066
  create: () => create2,
1064
1067
  deleteTable: () => deleteTable,
1065
1068
  find: () => find,
@@ -1659,6 +1662,232 @@ var lsiPhash = {
1659
1662
  name: "phash-lsi",
1660
1663
  sk: "phash"
1661
1664
  };
1665
+ var validateTableName = (tableName) => {
1666
+ const regex = /^[a-zA-Z0-9_.-]{3,255}$/;
1667
+ if (!regex.test(tableName)) {
1668
+ throw new Error(
1669
+ `tableName must follow AWS naming rules (3-255 length, and only the following characters: a-z, A-Z, 0-9, _-.) but received: ${tableName}`
1670
+ );
1671
+ }
1672
+ };
1673
+ var clearTableSchema = (table) => {
1674
+ const cleanTable = {
1675
+ TableName: table.TableName,
1676
+ AttributeDefinitions: table.AttributeDefinitions,
1677
+ KeySchema: table.KeySchema
1678
+ };
1679
+ if (table.BillingModeSummary?.BillingMode) {
1680
+ cleanTable.BillingMode = table.BillingModeSummary.BillingMode;
1681
+ }
1682
+ if (table.ProvisionedThroughput && table.BillingModeSummary?.BillingMode !== "PAY_PER_REQUEST") {
1683
+ cleanTable.ProvisionedThroughput = {
1684
+ ReadCapacityUnits: table.ProvisionedThroughput.ReadCapacityUnits,
1685
+ WriteCapacityUnits: table.ProvisionedThroughput.WriteCapacityUnits
1686
+ };
1687
+ }
1688
+ if (table.LocalSecondaryIndexes && table.LocalSecondaryIndexes.length > 0) {
1689
+ cleanTable.LocalSecondaryIndexes = table.LocalSecondaryIndexes.map(
1690
+ (lsi3) => ({
1691
+ IndexName: lsi3.IndexName,
1692
+ KeySchema: lsi3.KeySchema,
1693
+ Projection: lsi3.Projection
1694
+ })
1695
+ );
1696
+ }
1697
+ if (table.GlobalSecondaryIndexes && table.GlobalSecondaryIndexes.length > 0) {
1698
+ cleanTable.GlobalSecondaryIndexes = table.GlobalSecondaryIndexes.map(
1699
+ (gsi) => {
1700
+ const cleanGsi = {
1701
+ IndexName: gsi.IndexName,
1702
+ KeySchema: gsi.KeySchema,
1703
+ Projection: gsi.Projection
1704
+ };
1705
+ if (gsi.ProvisionedThroughput && table.BillingModeSummary?.BillingMode !== "PAY_PER_REQUEST") {
1706
+ cleanGsi.ProvisionedThroughput = {
1707
+ ReadCapacityUnits: gsi.ProvisionedThroughput.ReadCapacityUnits,
1708
+ WriteCapacityUnits: gsi.ProvisionedThroughput.WriteCapacityUnits
1709
+ };
1710
+ }
1711
+ return cleanGsi;
1712
+ }
1713
+ );
1714
+ }
1715
+ if (table.SSEDescription) {
1716
+ cleanTable.SSESpecification = {
1717
+ Enabled: table.SSEDescription.Status === "ENABLED" || table.SSEDescription.Status === "ENABLING"
1718
+ };
1719
+ if (table.SSEDescription.SSEType === "KMS" && table.SSEDescription.KMSMasterKeyArn) {
1720
+ cleanTable.SSESpecification.SSEType = "KMS";
1721
+ cleanTable.SSESpecification.KMSMasterKeyId = table.SSEDescription.KMSMasterKeyArn;
1722
+ }
1723
+ }
1724
+ if (table.StreamSpecification) {
1725
+ cleanTable.StreamSpecification = {
1726
+ StreamEnabled: table.StreamSpecification.StreamEnabled,
1727
+ StreamViewType: table.StreamSpecification.StreamViewType
1728
+ };
1729
+ }
1730
+ return cleanTable;
1731
+ };
1732
+ var waitForActive = async (tableName, client, log2) => {
1733
+ if (log2) {
1734
+ signale__default.default.log(`Waiting for table "${tableName}" to become active...`);
1735
+ }
1736
+ await clientDynamodb.waitUntilTableExists(
1737
+ {
1738
+ client,
1739
+ // Check every 5 seconds
1740
+ minDelay: 5,
1741
+ maxDelay: 5,
1742
+ // Wait up to 10 minutes
1743
+ maxWaitTime: 600
1744
+ },
1745
+ { TableName: tableName }
1746
+ );
1747
+ if (log2) signale__default.default.success(`Table "${tableName}" is now active!`);
1748
+ };
1749
+ var sleep4 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1750
+ var copyTable = async (props) => {
1751
+ try {
1752
+ validateTableName(props.source.tableName);
1753
+ validateTableName(props.destination.tableName);
1754
+ const sourceDynamoClient = new clientDynamodb.DynamoDBClient({
1755
+ ...props.source.config,
1756
+ region: props.source.config?.region || "us-east-1"
1757
+ });
1758
+ const sourceDocClient = libDynamodb.DynamoDBDocumentClient.from(sourceDynamoClient);
1759
+ const destDynamoClient = new clientDynamodb.DynamoDBClient({
1760
+ ...props.destination.config,
1761
+ region: props.destination.config?.region || "us-east-1"
1762
+ });
1763
+ const destDocClient = libDynamodb.DynamoDBDocumentClient.from(destDynamoClient);
1764
+ let counter = 0;
1765
+ if (props.create) {
1766
+ try {
1767
+ const sourceCommand = new clientDynamodb.DescribeTableCommand({
1768
+ TableName: props.source.tableName
1769
+ });
1770
+ const sourceResponse = await sourceDynamoClient.send(sourceCommand);
1771
+ if (sourceResponse.Table?.TableStatus !== "ACTIVE") {
1772
+ throw new Error("Source table is not active");
1773
+ }
1774
+ const cleanedTable = clearTableSchema(sourceResponse.Table);
1775
+ cleanedTable.TableName = props.destination.tableName;
1776
+ try {
1777
+ const createCommand = new clientDynamodb.CreateTableCommand(cleanedTable);
1778
+ await destDynamoClient.send(createCommand);
1779
+ } catch (error2) {
1780
+ if (error2 instanceof Error && error2.name !== "ResourceInUseException") {
1781
+ throw error2;
1782
+ }
1783
+ }
1784
+ await waitForActive(
1785
+ props.destination.tableName,
1786
+ destDynamoClient,
1787
+ props.log
1788
+ );
1789
+ if (props.schemaOnly) {
1790
+ return { count: 0, status: "SUCCESS", schemaOnly: true };
1791
+ }
1792
+ if (props.continuousBackups) {
1793
+ try {
1794
+ const sourceBackupsCommand = new clientDynamodb.DescribeContinuousBackupsCommand({
1795
+ TableName: props.source.tableName
1796
+ });
1797
+ const sourceBackupsResponse = await sourceDynamoClient.send(
1798
+ sourceBackupsCommand
1799
+ );
1800
+ if (sourceBackupsResponse.ContinuousBackupsDescription?.ContinuousBackupsStatus === "ENABLED") {
1801
+ const updateBackupsCommand = new clientDynamodb.UpdateContinuousBackupsCommand({
1802
+ TableName: props.destination.tableName,
1803
+ PointInTimeRecoverySpecification: {
1804
+ PointInTimeRecoveryEnabled: true
1805
+ }
1806
+ });
1807
+ await destDynamoClient.send(updateBackupsCommand);
1808
+ }
1809
+ } catch (error2) {
1810
+ console.warn("Failed to enable continuous backups:", error2);
1811
+ }
1812
+ }
1813
+ } catch (error2) {
1814
+ signale__default.default.error({ error: error2 });
1815
+ return { count: 0, status: "FAIL" };
1816
+ }
1817
+ } else {
1818
+ try {
1819
+ const sourceCommand = new clientDynamodb.DescribeTableCommand({
1820
+ TableName: props.source.tableName
1821
+ });
1822
+ const sourceResponse = await sourceDynamoClient.send(sourceCommand);
1823
+ if (sourceResponse.Table?.TableStatus !== "ACTIVE") {
1824
+ throw new Error("Source table is not active");
1825
+ }
1826
+ const destCommand = new clientDynamodb.DescribeTableCommand({
1827
+ TableName: props.destination.tableName
1828
+ });
1829
+ const destResponse = await destDynamoClient.send(destCommand);
1830
+ if (destResponse.Table?.TableStatus !== "ACTIVE") {
1831
+ throw new Error("Destination table is not active");
1832
+ }
1833
+ } catch (error2) {
1834
+ signale__default.default.error({ error: error2 });
1835
+ return { count: 0, status: "FAIL" };
1836
+ }
1837
+ }
1838
+ let lastEvaluatedKey = void 0;
1839
+ while (true) {
1840
+ const scanInput = {
1841
+ TableName: props.source.tableName,
1842
+ Limit: 25,
1843
+ // Process 25 items at a time
1844
+ ExclusiveStartKey: lastEvaluatedKey
1845
+ };
1846
+ const scanCommand = new libDynamodb.ScanCommand(scanInput);
1847
+ const scanResponse = await sourceDocClient.send(
1848
+ scanCommand
1849
+ );
1850
+ const items = scanResponse.Items || [];
1851
+ if (items.length === 0) {
1852
+ break;
1853
+ }
1854
+ const writeRequests = items.map(
1855
+ (item2, index) => ({
1856
+ PutRequest: {
1857
+ Item: props.transform ? props.transform(item2, index) : item2
1858
+ }
1859
+ })
1860
+ );
1861
+ let retries = 0;
1862
+ let unprocessedItems = writeRequests;
1863
+ while (unprocessedItems.length > 0) {
1864
+ const batchWriteCommand = new libDynamodb.BatchWriteCommand({
1865
+ RequestItems: {
1866
+ [props.destination.tableName]: unprocessedItems
1867
+ }
1868
+ });
1869
+ const batchResponse = await destDocClient.send(batchWriteCommand);
1870
+ const stillUnprocessed = batchResponse.UnprocessedItems?.[props.destination.tableName] || [];
1871
+ counter += unprocessedItems.length - stillUnprocessed.length;
1872
+ if (stillUnprocessed.length > 0) {
1873
+ retries++;
1874
+ unprocessedItems = stillUnprocessed;
1875
+ await sleep4(2 * retries * 100);
1876
+ } else {
1877
+ unprocessedItems = [];
1878
+ }
1879
+ }
1880
+ process.stdout.write(`\rCopied ${counter} items`);
1881
+ lastEvaluatedKey = scanResponse.LastEvaluatedKey;
1882
+ if (!lastEvaluatedKey) break;
1883
+ }
1884
+ process.stdout.write("\n");
1885
+ return { count: counter, status: "SUCCESS" };
1886
+ } catch (error2) {
1887
+ console.error("Copy operation failed:", error2);
1888
+ return { count: 0, status: "FAIL" };
1889
+ }
1890
+ };
1662
1891
 
1663
1892
  // src/server/aws/sns/index.ts
1664
1893
  var sns_exports = {};
@@ -1765,9 +1994,11 @@ var send2 = (ses, email) => {
1765
1994
  // src/server/aws/s3/index.ts
1766
1995
  var s3_exports = {};
1767
1996
  __export(s3_exports, {
1768
- bucketHandle: () => bucketHandle,
1997
+ bucketExists: () => bucketExists,
1769
1998
  create: () => create6,
1999
+ createBucket: () => createBucket,
1770
2000
  createSignedUrl: () => createSignedUrl,
2001
+ deleteBucket: () => deleteBucket,
1771
2002
  deleteFile: () => deleteFile,
1772
2003
  getFile: () => getFile,
1773
2004
  getFileMetadata: () => getFileMetadata,
@@ -1878,31 +2109,28 @@ var getFileMetadata = async (s3, props) => {
1878
2109
  throw err;
1879
2110
  }
1880
2111
  };
1881
- async function bucketHandle(s3, props) {
1882
- const Bucket = props.bucketName;
1883
- if (props.action === "create") {
1884
- const command = new clientS3.CreateBucketCommand({ Bucket });
1885
- return s3.client.send(command);
1886
- } else if (props.action === "delete") {
1887
- const command = new clientS3.DeleteBucketCommand({ Bucket });
1888
- return s3.client.send(command);
1889
- } else if (props.action === "exists") {
1890
- try {
1891
- const command = new clientS3.HeadBucketCommand({ Bucket });
1892
- await s3.client.send(command);
1893
- return true;
1894
- } catch (err) {
1895
- const awsError = err;
1896
- if (awsError.$metadata?.httpStatusCode === 404 || awsError.$metadata?.httpStatusCode === 400) {
1897
- return false;
1898
- }
1899
- console.log(err);
1900
- throw err;
2112
+ var createBucket = (s3, bucketName) => {
2113
+ const command = new clientS3.CreateBucketCommand({ Bucket: bucketName });
2114
+ return s3.client.send(command);
2115
+ };
2116
+ var deleteBucket = (s3, bucketName) => {
2117
+ const command = new clientS3.DeleteBucketCommand({ Bucket: bucketName });
2118
+ return s3.client.send(command);
2119
+ };
2120
+ var bucketExists = async (s3, bucketName) => {
2121
+ try {
2122
+ const command = new clientS3.HeadBucketCommand({ Bucket: bucketName });
2123
+ await s3.client.send(command);
2124
+ return true;
2125
+ } catch (err) {
2126
+ const awsError = err;
2127
+ if (awsError.$metadata?.httpStatusCode === 404 || awsError.$metadata?.httpStatusCode === 400) {
2128
+ return false;
1901
2129
  }
1902
- } else {
1903
- throw new Error(`[error] [bucketHandle] invalid action: ${props.action}`);
2130
+ console.log(err);
2131
+ throw err;
1904
2132
  }
1905
- }
2133
+ };
1906
2134
  var migrateBucketContents = async (s3, buckets) => {
1907
2135
  const command = new clientS3.ListObjectsV2Command({ Bucket: buckets.from });
1908
2136
  const srcFiles = await s3.client.send(command);
@@ -3,7 +3,8 @@ import awaitSpawn from 'await-spawn';
3
3
  import { promises } from 'fs';
4
4
  import { ArkErrors } from 'arktype';
5
5
  import { DynamoDBDocumentClient, ScanCommand, QueryCommand, DeleteCommand, PutCommand, BatchGetCommand, TransactWriteCommand, BatchWriteCommand } from '@aws-sdk/lib-dynamodb';
6
- import { DynamoDBClient, DeleteTableCommand, DescribeTableCommand, DynamoDBServiceException } from '@aws-sdk/client-dynamodb';
6
+ import { DynamoDBClient, DeleteTableCommand, DescribeTableCommand, DynamoDBServiceException, CreateTableCommand, DescribeContinuousBackupsCommand, UpdateContinuousBackupsCommand, waitUntilTableExists } from '@aws-sdk/client-dynamodb';
7
+ import signale from 'signale';
7
8
  import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';
8
9
  import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';
9
10
  import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
@@ -1053,6 +1054,7 @@ __export(ddb_exports, {
1053
1054
  batchDelete: () => batchDelete,
1054
1055
  batchGet: () => batchGet,
1055
1056
  batchWrite: () => batchWrite,
1057
+ copyTable: () => copyTable,
1056
1058
  create: () => create2,
1057
1059
  deleteTable: () => deleteTable,
1058
1060
  find: () => find,
@@ -1652,6 +1654,232 @@ var lsiPhash = {
1652
1654
  name: "phash-lsi",
1653
1655
  sk: "phash"
1654
1656
  };
1657
+ var validateTableName = (tableName) => {
1658
+ const regex = /^[a-zA-Z0-9_.-]{3,255}$/;
1659
+ if (!regex.test(tableName)) {
1660
+ throw new Error(
1661
+ `tableName must follow AWS naming rules (3-255 length, and only the following characters: a-z, A-Z, 0-9, _-.) but received: ${tableName}`
1662
+ );
1663
+ }
1664
+ };
1665
+ var clearTableSchema = (table) => {
1666
+ const cleanTable = {
1667
+ TableName: table.TableName,
1668
+ AttributeDefinitions: table.AttributeDefinitions,
1669
+ KeySchema: table.KeySchema
1670
+ };
1671
+ if (table.BillingModeSummary?.BillingMode) {
1672
+ cleanTable.BillingMode = table.BillingModeSummary.BillingMode;
1673
+ }
1674
+ if (table.ProvisionedThroughput && table.BillingModeSummary?.BillingMode !== "PAY_PER_REQUEST") {
1675
+ cleanTable.ProvisionedThroughput = {
1676
+ ReadCapacityUnits: table.ProvisionedThroughput.ReadCapacityUnits,
1677
+ WriteCapacityUnits: table.ProvisionedThroughput.WriteCapacityUnits
1678
+ };
1679
+ }
1680
+ if (table.LocalSecondaryIndexes && table.LocalSecondaryIndexes.length > 0) {
1681
+ cleanTable.LocalSecondaryIndexes = table.LocalSecondaryIndexes.map(
1682
+ (lsi3) => ({
1683
+ IndexName: lsi3.IndexName,
1684
+ KeySchema: lsi3.KeySchema,
1685
+ Projection: lsi3.Projection
1686
+ })
1687
+ );
1688
+ }
1689
+ if (table.GlobalSecondaryIndexes && table.GlobalSecondaryIndexes.length > 0) {
1690
+ cleanTable.GlobalSecondaryIndexes = table.GlobalSecondaryIndexes.map(
1691
+ (gsi) => {
1692
+ const cleanGsi = {
1693
+ IndexName: gsi.IndexName,
1694
+ KeySchema: gsi.KeySchema,
1695
+ Projection: gsi.Projection
1696
+ };
1697
+ if (gsi.ProvisionedThroughput && table.BillingModeSummary?.BillingMode !== "PAY_PER_REQUEST") {
1698
+ cleanGsi.ProvisionedThroughput = {
1699
+ ReadCapacityUnits: gsi.ProvisionedThroughput.ReadCapacityUnits,
1700
+ WriteCapacityUnits: gsi.ProvisionedThroughput.WriteCapacityUnits
1701
+ };
1702
+ }
1703
+ return cleanGsi;
1704
+ }
1705
+ );
1706
+ }
1707
+ if (table.SSEDescription) {
1708
+ cleanTable.SSESpecification = {
1709
+ Enabled: table.SSEDescription.Status === "ENABLED" || table.SSEDescription.Status === "ENABLING"
1710
+ };
1711
+ if (table.SSEDescription.SSEType === "KMS" && table.SSEDescription.KMSMasterKeyArn) {
1712
+ cleanTable.SSESpecification.SSEType = "KMS";
1713
+ cleanTable.SSESpecification.KMSMasterKeyId = table.SSEDescription.KMSMasterKeyArn;
1714
+ }
1715
+ }
1716
+ if (table.StreamSpecification) {
1717
+ cleanTable.StreamSpecification = {
1718
+ StreamEnabled: table.StreamSpecification.StreamEnabled,
1719
+ StreamViewType: table.StreamSpecification.StreamViewType
1720
+ };
1721
+ }
1722
+ return cleanTable;
1723
+ };
1724
+ var waitForActive = async (tableName, client, log2) => {
1725
+ if (log2) {
1726
+ signale.log(`Waiting for table "${tableName}" to become active...`);
1727
+ }
1728
+ await waitUntilTableExists(
1729
+ {
1730
+ client,
1731
+ // Check every 5 seconds
1732
+ minDelay: 5,
1733
+ maxDelay: 5,
1734
+ // Wait up to 10 minutes
1735
+ maxWaitTime: 600
1736
+ },
1737
+ { TableName: tableName }
1738
+ );
1739
+ if (log2) signale.success(`Table "${tableName}" is now active!`);
1740
+ };
1741
+ var sleep4 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1742
+ var copyTable = async (props) => {
1743
+ try {
1744
+ validateTableName(props.source.tableName);
1745
+ validateTableName(props.destination.tableName);
1746
+ const sourceDynamoClient = new DynamoDBClient({
1747
+ ...props.source.config,
1748
+ region: props.source.config?.region || "us-east-1"
1749
+ });
1750
+ const sourceDocClient = DynamoDBDocumentClient.from(sourceDynamoClient);
1751
+ const destDynamoClient = new DynamoDBClient({
1752
+ ...props.destination.config,
1753
+ region: props.destination.config?.region || "us-east-1"
1754
+ });
1755
+ const destDocClient = DynamoDBDocumentClient.from(destDynamoClient);
1756
+ let counter = 0;
1757
+ if (props.create) {
1758
+ try {
1759
+ const sourceCommand = new DescribeTableCommand({
1760
+ TableName: props.source.tableName
1761
+ });
1762
+ const sourceResponse = await sourceDynamoClient.send(sourceCommand);
1763
+ if (sourceResponse.Table?.TableStatus !== "ACTIVE") {
1764
+ throw new Error("Source table is not active");
1765
+ }
1766
+ const cleanedTable = clearTableSchema(sourceResponse.Table);
1767
+ cleanedTable.TableName = props.destination.tableName;
1768
+ try {
1769
+ const createCommand = new CreateTableCommand(cleanedTable);
1770
+ await destDynamoClient.send(createCommand);
1771
+ } catch (error2) {
1772
+ if (error2 instanceof Error && error2.name !== "ResourceInUseException") {
1773
+ throw error2;
1774
+ }
1775
+ }
1776
+ await waitForActive(
1777
+ props.destination.tableName,
1778
+ destDynamoClient,
1779
+ props.log
1780
+ );
1781
+ if (props.schemaOnly) {
1782
+ return { count: 0, status: "SUCCESS", schemaOnly: true };
1783
+ }
1784
+ if (props.continuousBackups) {
1785
+ try {
1786
+ const sourceBackupsCommand = new DescribeContinuousBackupsCommand({
1787
+ TableName: props.source.tableName
1788
+ });
1789
+ const sourceBackupsResponse = await sourceDynamoClient.send(
1790
+ sourceBackupsCommand
1791
+ );
1792
+ if (sourceBackupsResponse.ContinuousBackupsDescription?.ContinuousBackupsStatus === "ENABLED") {
1793
+ const updateBackupsCommand = new UpdateContinuousBackupsCommand({
1794
+ TableName: props.destination.tableName,
1795
+ PointInTimeRecoverySpecification: {
1796
+ PointInTimeRecoveryEnabled: true
1797
+ }
1798
+ });
1799
+ await destDynamoClient.send(updateBackupsCommand);
1800
+ }
1801
+ } catch (error2) {
1802
+ console.warn("Failed to enable continuous backups:", error2);
1803
+ }
1804
+ }
1805
+ } catch (error2) {
1806
+ signale.error({ error: error2 });
1807
+ return { count: 0, status: "FAIL" };
1808
+ }
1809
+ } else {
1810
+ try {
1811
+ const sourceCommand = new DescribeTableCommand({
1812
+ TableName: props.source.tableName
1813
+ });
1814
+ const sourceResponse = await sourceDynamoClient.send(sourceCommand);
1815
+ if (sourceResponse.Table?.TableStatus !== "ACTIVE") {
1816
+ throw new Error("Source table is not active");
1817
+ }
1818
+ const destCommand = new DescribeTableCommand({
1819
+ TableName: props.destination.tableName
1820
+ });
1821
+ const destResponse = await destDynamoClient.send(destCommand);
1822
+ if (destResponse.Table?.TableStatus !== "ACTIVE") {
1823
+ throw new Error("Destination table is not active");
1824
+ }
1825
+ } catch (error2) {
1826
+ signale.error({ error: error2 });
1827
+ return { count: 0, status: "FAIL" };
1828
+ }
1829
+ }
1830
+ let lastEvaluatedKey = void 0;
1831
+ while (true) {
1832
+ const scanInput = {
1833
+ TableName: props.source.tableName,
1834
+ Limit: 25,
1835
+ // Process 25 items at a time
1836
+ ExclusiveStartKey: lastEvaluatedKey
1837
+ };
1838
+ const scanCommand = new ScanCommand(scanInput);
1839
+ const scanResponse = await sourceDocClient.send(
1840
+ scanCommand
1841
+ );
1842
+ const items = scanResponse.Items || [];
1843
+ if (items.length === 0) {
1844
+ break;
1845
+ }
1846
+ const writeRequests = items.map(
1847
+ (item2, index) => ({
1848
+ PutRequest: {
1849
+ Item: props.transform ? props.transform(item2, index) : item2
1850
+ }
1851
+ })
1852
+ );
1853
+ let retries = 0;
1854
+ let unprocessedItems = writeRequests;
1855
+ while (unprocessedItems.length > 0) {
1856
+ const batchWriteCommand = new BatchWriteCommand({
1857
+ RequestItems: {
1858
+ [props.destination.tableName]: unprocessedItems
1859
+ }
1860
+ });
1861
+ const batchResponse = await destDocClient.send(batchWriteCommand);
1862
+ const stillUnprocessed = batchResponse.UnprocessedItems?.[props.destination.tableName] || [];
1863
+ counter += unprocessedItems.length - stillUnprocessed.length;
1864
+ if (stillUnprocessed.length > 0) {
1865
+ retries++;
1866
+ unprocessedItems = stillUnprocessed;
1867
+ await sleep4(2 * retries * 100);
1868
+ } else {
1869
+ unprocessedItems = [];
1870
+ }
1871
+ }
1872
+ process.stdout.write(`\rCopied ${counter} items`);
1873
+ lastEvaluatedKey = scanResponse.LastEvaluatedKey;
1874
+ if (!lastEvaluatedKey) break;
1875
+ }
1876
+ process.stdout.write("\n");
1877
+ return { count: counter, status: "SUCCESS" };
1878
+ } catch (error2) {
1879
+ console.error("Copy operation failed:", error2);
1880
+ return { count: 0, status: "FAIL" };
1881
+ }
1882
+ };
1655
1883
 
1656
1884
  // src/server/aws/sns/index.ts
1657
1885
  var sns_exports = {};
@@ -1758,9 +1986,11 @@ var send2 = (ses, email) => {
1758
1986
  // src/server/aws/s3/index.ts
1759
1987
  var s3_exports = {};
1760
1988
  __export(s3_exports, {
1761
- bucketHandle: () => bucketHandle,
1989
+ bucketExists: () => bucketExists,
1762
1990
  create: () => create6,
1991
+ createBucket: () => createBucket,
1763
1992
  createSignedUrl: () => createSignedUrl,
1993
+ deleteBucket: () => deleteBucket,
1764
1994
  deleteFile: () => deleteFile,
1765
1995
  getFile: () => getFile,
1766
1996
  getFileMetadata: () => getFileMetadata,
@@ -1871,31 +2101,28 @@ var getFileMetadata = async (s3, props) => {
1871
2101
  throw err;
1872
2102
  }
1873
2103
  };
1874
- async function bucketHandle(s3, props) {
1875
- const Bucket = props.bucketName;
1876
- if (props.action === "create") {
1877
- const command = new CreateBucketCommand({ Bucket });
1878
- return s3.client.send(command);
1879
- } else if (props.action === "delete") {
1880
- const command = new DeleteBucketCommand({ Bucket });
1881
- return s3.client.send(command);
1882
- } else if (props.action === "exists") {
1883
- try {
1884
- const command = new HeadBucketCommand({ Bucket });
1885
- await s3.client.send(command);
1886
- return true;
1887
- } catch (err) {
1888
- const awsError = err;
1889
- if (awsError.$metadata?.httpStatusCode === 404 || awsError.$metadata?.httpStatusCode === 400) {
1890
- return false;
1891
- }
1892
- console.log(err);
1893
- throw err;
2104
+ var createBucket = (s3, bucketName) => {
2105
+ const command = new CreateBucketCommand({ Bucket: bucketName });
2106
+ return s3.client.send(command);
2107
+ };
2108
+ var deleteBucket = (s3, bucketName) => {
2109
+ const command = new DeleteBucketCommand({ Bucket: bucketName });
2110
+ return s3.client.send(command);
2111
+ };
2112
+ var bucketExists = async (s3, bucketName) => {
2113
+ try {
2114
+ const command = new HeadBucketCommand({ Bucket: bucketName });
2115
+ await s3.client.send(command);
2116
+ return true;
2117
+ } catch (err) {
2118
+ const awsError = err;
2119
+ if (awsError.$metadata?.httpStatusCode === 404 || awsError.$metadata?.httpStatusCode === 400) {
2120
+ return false;
1894
2121
  }
1895
- } else {
1896
- throw new Error(`[error] [bucketHandle] invalid action: ${props.action}`);
2122
+ console.log(err);
2123
+ throw err;
1897
2124
  }
1898
- }
2125
+ };
1899
2126
  var migrateBucketContents = async (s3, buckets) => {
1900
2127
  const command = new ListObjectsV2Command({ Bucket: buckets.from });
1901
2128
  const srcFiles = await s3.client.send(command);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qstd",
3
- "version": "0.3.53",
3
+ "version": "0.3.55",
4
4
  "description": "Standard Block component and utilities library with Panda CSS",
5
5
  "author": "malin1",
6
6
  "license": "MIT",
@@ -90,7 +90,8 @@
90
90
  "react-loader-spinner": "^6.1.6",
91
91
  "react-spinners": "^0.17.0",
92
92
  "use-immer": "^0.11.0",
93
- "await-spawn": "^4.0.2"
93
+ "await-spawn": "^4.0.2",
94
+ "signale": "^1.4.0"
94
95
  },
95
96
  "devDependencies": {
96
97
  "@eslint/js": "^9.39.1",
@@ -101,6 +102,7 @@
101
102
  "@types/node": "^22.10.5",
102
103
  "@types/react": "^19.1.12",
103
104
  "@types/react-dom": "^19.1.8",
105
+ "@types/signale": "^1.4.7",
104
106
  "eslint": "^9.39.1",
105
107
  "eslint-plugin-react-hooks": "^7.0.1",
106
108
  "globals": "^16.2.0",