qstd 0.3.52 → 0.3.54

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,10 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  var dateFns = require('date-fns');
4
+ var awaitSpawn = require('await-spawn');
4
5
  var fs = require('fs');
5
6
  var arktype = require('arktype');
6
7
  var libDynamodb = require('@aws-sdk/lib-dynamodb');
7
8
  var clientDynamodb = require('@aws-sdk/client-dynamodb');
9
+ var signale = require('signale');
8
10
  var clientSns = require('@aws-sdk/client-sns');
9
11
  var clientSqs = require('@aws-sdk/client-sqs');
10
12
  var clientSes = require('@aws-sdk/client-ses');
@@ -13,6 +15,11 @@ var s3RequestPresigner = require('@aws-sdk/s3-request-presigner');
13
15
  var s3PresignedPost = require('@aws-sdk/s3-presigned-post');
14
16
 
15
17
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
18
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
19
+
20
+ var awaitSpawn__default = /*#__PURE__*/_interopDefault(awaitSpawn);
21
+ var signale__default = /*#__PURE__*/_interopDefault(signale);
22
+
16
23
  var __defProp = Object.defineProperty;
17
24
  var __export = (target, all) => {
18
25
  for (var name in all)
@@ -922,6 +929,13 @@ function remove(path, body, opts) {
922
929
  );
923
930
  }
924
931
 
932
+ // src/server/os/index.ts
933
+ var os_exports = {};
934
+ __export(os_exports, {
935
+ spawn: () => spawn
936
+ });
937
+ var spawn = (cmd) => awaitSpawn__default.default(cmd, { shell: true, stdio: "inherit" });
938
+
925
939
  // src/server/file/index.ts
926
940
  var file_exports = {};
927
941
  __export(file_exports, {
@@ -1048,6 +1062,7 @@ __export(ddb_exports, {
1048
1062
  batchDelete: () => batchDelete,
1049
1063
  batchGet: () => batchGet,
1050
1064
  batchWrite: () => batchWrite,
1065
+ copyTable: () => copyTable,
1051
1066
  create: () => create2,
1052
1067
  deleteTable: () => deleteTable,
1053
1068
  find: () => find,
@@ -1647,6 +1662,232 @@ var lsiPhash = {
1647
1662
  name: "phash-lsi",
1648
1663
  sk: "phash"
1649
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
+ };
1650
1891
 
1651
1892
  // src/server/aws/sns/index.ts
1652
1893
  var sns_exports = {};
@@ -1933,6 +2174,7 @@ exports.Lambda = lambda_exports;
1933
2174
  exports.List = list_exports;
1934
2175
  exports.Log = log_exports;
1935
2176
  exports.Money = money_exports;
2177
+ exports.Os = os_exports;
1936
2178
  exports.Random = random_exports;
1937
2179
  exports.S3 = s3_exports;
1938
2180
  exports.SES = ses_exports;
@@ -8,6 +8,7 @@ export * as Flow from "../shared/flow";
8
8
  export * as Random from "../shared/random";
9
9
  export * as Log from "../shared/log";
10
10
  export * as Api from "../shared/api";
11
+ export * as Os from "./os";
11
12
  export * as File from "./file";
12
13
  export * as Lambda from "./aws/lambda";
13
14
  export * as DDB from "./aws/ddb";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,GAAG,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,GAAG,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,KAAK,MAAM,iBAAiB,CAAC;AACzC,OAAO,KAAK,IAAI,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAC3C,OAAO,KAAK,GAAG,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,GAAG,MAAM,eAAe,CAAC;AAGrC,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,GAAG,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,GAAG,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,GAAG,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,GAAG,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,GAAG,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,GAAG,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,KAAK,MAAM,iBAAiB,CAAC;AACzC,OAAO,KAAK,IAAI,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAC3C,OAAO,KAAK,GAAG,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,GAAG,MAAM,eAAe,CAAC;AAGrC,OAAO,KAAK,EAAE,MAAM,MAAM,CAAC;AAC3B,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,GAAG,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,GAAG,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,GAAG,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,GAAG,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,EAAE,MAAM,UAAU,CAAC"}
@@ -1,8 +1,10 @@
1
1
  import { format, formatDistanceToNow, formatISO, isSameYear, isSameMonth, isSameDay, addSeconds, addMinutes, addHours, addDays, addWeeks, addBusinessDays, addMonths, addYears } from 'date-fns';
2
+ import awaitSpawn from 'await-spawn';
2
3
  import { promises } from 'fs';
3
4
  import { ArkErrors } from 'arktype';
4
5
  import { DynamoDBDocumentClient, ScanCommand, QueryCommand, DeleteCommand, PutCommand, BatchGetCommand, TransactWriteCommand, BatchWriteCommand } from '@aws-sdk/lib-dynamodb';
5
- 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';
6
8
  import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';
7
9
  import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';
8
10
  import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
@@ -919,6 +921,13 @@ function remove(path, body, opts) {
919
921
  );
920
922
  }
921
923
 
924
+ // src/server/os/index.ts
925
+ var os_exports = {};
926
+ __export(os_exports, {
927
+ spawn: () => spawn
928
+ });
929
+ var spawn = (cmd) => awaitSpawn(cmd, { shell: true, stdio: "inherit" });
930
+
922
931
  // src/server/file/index.ts
923
932
  var file_exports = {};
924
933
  __export(file_exports, {
@@ -1045,6 +1054,7 @@ __export(ddb_exports, {
1045
1054
  batchDelete: () => batchDelete,
1046
1055
  batchGet: () => batchGet,
1047
1056
  batchWrite: () => batchWrite,
1057
+ copyTable: () => copyTable,
1048
1058
  create: () => create2,
1049
1059
  deleteTable: () => deleteTable,
1050
1060
  find: () => find,
@@ -1644,6 +1654,232 @@ var lsiPhash = {
1644
1654
  name: "phash-lsi",
1645
1655
  sk: "phash"
1646
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
+ };
1647
1883
 
1648
1884
  // src/server/aws/sns/index.ts
1649
1885
  var sns_exports = {};
@@ -1920,4 +2156,4 @@ var recordsFromSqs = (body) => {
1920
2156
  }
1921
2157
  };
1922
2158
 
1923
- export { api_exports as Api, ddb_exports as DDB, dict_exports as Dict, file_exports as File, flow_exports as Flow, int_exports as Int, lambda_exports as Lambda, list_exports as List, log_exports as Log, money_exports as Money, random_exports as Random, s3_exports as S3, ses_exports as SES, sns_exports as SNS, sqs_exports as SQS, str_exports as Str, time_exports as Time };
2159
+ export { api_exports as Api, ddb_exports as DDB, dict_exports as Dict, file_exports as File, flow_exports as Flow, int_exports as Int, lambda_exports as Lambda, list_exports as List, log_exports as Log, money_exports as Money, os_exports as Os, random_exports as Random, s3_exports as S3, ses_exports as SES, sns_exports as SNS, sqs_exports as SQS, str_exports as Str, time_exports as Time };
@@ -0,0 +1,11 @@
1
+ /** Allows any string while providing autocomplete for known commands */
2
+ type Command = "python3" | "node" | "bun" | "pnpm" | "yarn" | "npm" | (string & {});
3
+ /**
4
+ * A command line spawn that awaits properly.
5
+ * Uses shell execution and inherits stdio for full console output.
6
+ * @param cmd - The shell command to execute
7
+ * @returns Promise that resolves when the command completes
8
+ */
9
+ export declare const spawn: (cmd: Command) => Promise<Buffer>;
10
+ export {};
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/os/index.ts"],"names":[],"mappings":"AAEA,wEAAwE;AACxE,KAAK,OAAO,GACR,SAAS,GACT,MAAM,GACN,KAAK,GACL,MAAM,GACN,MAAM,GACN,KAAK,GACL,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB;;;;;GAKG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,OAAO,KAAG,OAAO,CAAC,MAAM,CACC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qstd",
3
- "version": "0.3.52",
3
+ "version": "0.3.54",
4
4
  "description": "Standard Block component and utilities library with Panda CSS",
5
5
  "author": "malin1",
6
6
  "license": "MIT",
@@ -89,7 +89,9 @@
89
89
  "react-icons": "^5.5.0",
90
90
  "react-loader-spinner": "^6.1.6",
91
91
  "react-spinners": "^0.17.0",
92
- "use-immer": "^0.11.0"
92
+ "use-immer": "^0.11.0",
93
+ "await-spawn": "^4.0.2",
94
+ "signale": "^1.4.0"
93
95
  },
94
96
  "devDependencies": {
95
97
  "@eslint/js": "^9.39.1",
@@ -100,6 +102,7 @@
100
102
  "@types/node": "^22.10.5",
101
103
  "@types/react": "^19.1.12",
102
104
  "@types/react-dom": "^19.1.8",
105
+ "@types/signale": "^1.4.7",
103
106
  "eslint": "^9.39.1",
104
107
  "eslint-plugin-react-hooks": "^7.0.1",
105
108
  "globals": "^16.2.0",