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.
- package/dist/server/aws/ddb/copy-table.d.ts +51 -0
- package/dist/server/aws/ddb/copy-table.d.ts.map +1 -0
- package/dist/server/aws/ddb/index.d.ts +1 -0
- package/dist/server/aws/ddb/index.d.ts.map +1 -1
- package/dist/server/index.cjs +242 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +238 -2
- package/dist/server/os/index.d.ts +11 -0
- package/dist/server/os/index.d.ts.map +1 -0
- package/package.json +5 -2
|
@@ -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 +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"}
|
package/dist/server/index.cjs
CHANGED
|
@@ -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;
|
package/dist/server/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/server/index.js
CHANGED
|
@@ -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.
|
|
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",
|