s3db.js 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/.github/workflows/pipeline.yml +16 -0
  2. package/README.md +742 -0
  3. package/build/cache/avro.serializer.js +16 -0
  4. package/build/cache/json.serializer.js +7 -0
  5. package/build/cache/s3-cache.class.js +157 -0
  6. package/build/cache/s3-resource-cache.class.js +77 -0
  7. package/build/cache/serializers.type.js +8 -0
  8. package/build/errors.js +64 -0
  9. package/build/index.js +9 -0
  10. package/build/metadata.interface.js +2 -0
  11. package/build/plugin.interface.js +2 -0
  12. package/build/resource.class.js +485 -0
  13. package/build/resource.interface.js +2 -0
  14. package/build/s3-client.class.js +274 -0
  15. package/build/s3db-config.interface.js +2 -0
  16. package/build/s3db.class.js +185 -0
  17. package/build/stream/resource-ids-read-stream.class.js +100 -0
  18. package/build/stream/resource-ids-transformer.class.js +40 -0
  19. package/build/stream/resource-write-stream.class.js +76 -0
  20. package/build/validator.js +37 -0
  21. package/examples/1-bulk-insert.js +64 -0
  22. package/examples/2-read-stream.js +61 -0
  23. package/examples/3-read-stream-to-csv.js +57 -0
  24. package/examples/4-read-stream-to-zip.js +56 -0
  25. package/examples/5-write-stream.js +98 -0
  26. package/examples/6-jwt-tokens.js +124 -0
  27. package/examples/concerns/index.js +64 -0
  28. package/jest.config.ts +10 -0
  29. package/package.json +51 -0
  30. package/src/cache/avro.serializer.ts +12 -0
  31. package/src/cache/json.serializer.ts +4 -0
  32. package/src/cache/s3-cache.class.ts +155 -0
  33. package/src/cache/s3-resource-cache.class.ts +75 -0
  34. package/src/cache/serializers.type.ts +8 -0
  35. package/src/errors.ts +96 -0
  36. package/src/index.ts +4 -0
  37. package/src/metadata.interface.ts +4 -0
  38. package/src/plugin.interface.ts +4 -0
  39. package/src/resource.class.ts +531 -0
  40. package/src/resource.interface.ts +21 -0
  41. package/src/s3-client.class.ts +297 -0
  42. package/src/s3db-config.interface.ts +9 -0
  43. package/src/s3db.class.ts +215 -0
  44. package/src/stream/resource-ids-read-stream.class.ts +90 -0
  45. package/src/stream/resource-ids-transformer.class.ts +38 -0
  46. package/src/stream/resource-write-stream.class.ts +78 -0
  47. package/src/validator.ts +39 -0
  48. package/tests/cache.spec.ts +187 -0
  49. package/tests/concerns/index.ts +16 -0
  50. package/tests/config.spec.ts +29 -0
  51. package/tests/resources.spec.ts +197 -0
  52. package/tsconfig.json +111 -0
@@ -0,0 +1,274 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ const path = __importStar(require("path"));
39
+ const lodash_1 = require("lodash");
40
+ const nanoid_1 = require("nanoid");
41
+ const events_1 = __importDefault(require("events"));
42
+ const aws_sdk_1 = require("aws-sdk");
43
+ const promise_pool_1 = __importDefault(require("@supercharge/promise-pool"));
44
+ const errors_1 = require("./errors");
45
+ class S3Client extends events_1.default {
46
+ constructor({ connectionString, parallelism = 10, AwsS3, }) {
47
+ super();
48
+ this.id = (0, nanoid_1.nanoid)(7);
49
+ const uri = new URL(connectionString);
50
+ this.bucket = uri.hostname;
51
+ this.parallelism = parallelism;
52
+ let [, ...subpath] = uri.pathname.split("/");
53
+ this.keyPrefix = [...(subpath || [])].join("/");
54
+ this.client =
55
+ AwsS3 ||
56
+ new aws_sdk_1.S3({
57
+ credentials: new aws_sdk_1.Credentials({
58
+ accessKeyId: uri.username,
59
+ secretAccessKey: uri.password,
60
+ }),
61
+ });
62
+ }
63
+ /**
64
+ *
65
+ * @param param0
66
+ * @returns
67
+ */
68
+ getObject({ key }) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ try {
71
+ const options = {
72
+ Bucket: this.bucket,
73
+ Key: path.join(this.keyPrefix, key),
74
+ };
75
+ const response = yield this.client.getObject(options).promise();
76
+ this.emit("request", "getObject", options);
77
+ return response;
78
+ }
79
+ catch (error) {
80
+ if (error instanceof Error) {
81
+ if (error.name === "NoSuchKey") {
82
+ return Promise.reject(new errors_1.ClientNoSuchKey({ bucket: this.bucket, key }));
83
+ }
84
+ }
85
+ return Promise.reject(error);
86
+ }
87
+ });
88
+ }
89
+ /**
90
+ *
91
+ * @param param0
92
+ * @returns
93
+ */
94
+ putObject({ key, metadata, contentType, body, contentEncoding, }) {
95
+ return __awaiter(this, void 0, void 0, function* () {
96
+ try {
97
+ const options = {
98
+ Bucket: this.bucket,
99
+ Key: path.join(this.keyPrefix, key),
100
+ Metadata: Object.assign({}, metadata),
101
+ Body: body,
102
+ ContentType: contentType,
103
+ ContentEncoding: contentEncoding,
104
+ };
105
+ const response = yield this.client.putObject(options).promise();
106
+ this.emit("request", "putObject", options);
107
+ return response;
108
+ }
109
+ catch (error) {
110
+ this.emit("error", error);
111
+ return Promise.reject(error);
112
+ }
113
+ });
114
+ }
115
+ /**
116
+ * Proxy to AWS S3's headObject
117
+ * @param {Object} param
118
+ * @param {string} param.key
119
+ * @returns
120
+ */
121
+ headObject({ key }) {
122
+ return __awaiter(this, void 0, void 0, function* () {
123
+ try {
124
+ const options = {
125
+ Bucket: this.bucket,
126
+ Key: path.join(this.keyPrefix, key),
127
+ };
128
+ const response = yield this.client.headObject(options).promise();
129
+ this.emit("request", "headObject", options);
130
+ return response;
131
+ }
132
+ catch (error) {
133
+ if (error instanceof Error) {
134
+ if (error.name === "NoSuchKey" || error.name === "NotFound") {
135
+ return Promise.reject(new errors_1.ClientNoSuchKey({ bucket: this.bucket, key }));
136
+ }
137
+ }
138
+ this.emit("error", error);
139
+ return Promise.reject(error);
140
+ }
141
+ });
142
+ }
143
+ /**
144
+ * Proxy to AWS S3's deleteObject
145
+ * @param {Object} param
146
+ * @param {string} param.key
147
+ * @returns
148
+ */
149
+ deleteObject(key) {
150
+ return __awaiter(this, void 0, void 0, function* () {
151
+ try {
152
+ const options = {
153
+ Bucket: this.bucket,
154
+ Key: path.join(this.keyPrefix, key),
155
+ };
156
+ const response = yield this.client.deleteObject(options).promise();
157
+ this.emit("request", "deleteObject", options);
158
+ return response;
159
+ }
160
+ catch (error) {
161
+ this.emit("error", error);
162
+ if (error instanceof Error) {
163
+ if (error.name === "NoSuchKey") {
164
+ return Promise.reject(new errors_1.ClientNoSuchKey({ bucket: this.bucket, key }));
165
+ }
166
+ }
167
+ return Promise.reject(error);
168
+ }
169
+ });
170
+ }
171
+ /**
172
+ * Proxy to AWS S3's deleteObjects
173
+ * @param {Object} param
174
+ * @param {string} param.keys
175
+ * @returns
176
+ */
177
+ deleteObjects(keys) {
178
+ return __awaiter(this, void 0, void 0, function* () {
179
+ const packages = (0, lodash_1.chunk)(keys, 1000);
180
+ const { results, errors } = yield promise_pool_1.default.for(packages)
181
+ .withConcurrency(this.parallelism)
182
+ .process((keys) => __awaiter(this, void 0, void 0, function* () {
183
+ try {
184
+ const options = {
185
+ Bucket: this.bucket,
186
+ Delete: {
187
+ Objects: keys.map((key) => ({
188
+ Key: path.join(this.keyPrefix, key),
189
+ })),
190
+ },
191
+ };
192
+ const response = yield this.client.deleteObjects(options).promise();
193
+ this.emit("request", "deleteObjects", options);
194
+ return response;
195
+ }
196
+ catch (error) {
197
+ this.emit("error", error);
198
+ return Promise.reject(error);
199
+ }
200
+ }));
201
+ return {
202
+ deleted: results,
203
+ notFound: errors,
204
+ };
205
+ });
206
+ }
207
+ /**
208
+ *
209
+ * @param param0
210
+ * @returns
211
+ */
212
+ listObjects({ prefix, maxKeys = 1000, continuationToken, }) {
213
+ return __awaiter(this, void 0, void 0, function* () {
214
+ try {
215
+ const options = {
216
+ Bucket: this.bucket,
217
+ MaxKeys: maxKeys,
218
+ ContinuationToken: continuationToken,
219
+ Prefix: path.join(this.keyPrefix, prefix || ""),
220
+ };
221
+ const response = yield this.client.listObjectsV2(options).promise();
222
+ this.emit("request", "listObjectsV2", options);
223
+ return response;
224
+ }
225
+ catch (error) {
226
+ this.emit("error", error);
227
+ return Promise.reject(error);
228
+ }
229
+ });
230
+ }
231
+ count({ prefix } = {}) {
232
+ return __awaiter(this, void 0, void 0, function* () {
233
+ this.emit("request", "count", { prefix });
234
+ let count = 0;
235
+ let truncated = true;
236
+ let continuationToken;
237
+ while (truncated) {
238
+ const options = {
239
+ prefix,
240
+ continuationToken,
241
+ };
242
+ const res = yield this.listObjects(options);
243
+ count += res.KeyCount || 0;
244
+ truncated = res.IsTruncated || false;
245
+ continuationToken = res.NextContinuationToken;
246
+ }
247
+ return count;
248
+ });
249
+ }
250
+ getAllKeys({ prefix } = {}) {
251
+ return __awaiter(this, void 0, void 0, function* () {
252
+ this.emit("request", "getAllKeys", { prefix });
253
+ let keys = [];
254
+ let truncated = true;
255
+ let continuationToken;
256
+ while (truncated) {
257
+ const options = {
258
+ prefix,
259
+ continuationToken,
260
+ };
261
+ const res = yield this.listObjects(options);
262
+ if (res.Contents) {
263
+ keys = keys.concat(res.Contents.map((x) => x.Key));
264
+ }
265
+ truncated = res.IsTruncated || false;
266
+ continuationToken = res.NextContinuationToken;
267
+ }
268
+ return keys
269
+ .map((x) => x.replace(this.keyPrefix, ""))
270
+ .map((x) => (x.startsWith("/") ? x.replace(`/`, "") : x));
271
+ });
272
+ }
273
+ }
274
+ exports.default = S3Client;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const flat_1 = require("flat");
16
+ const lodash_1 = require("lodash");
17
+ const events_1 = __importDefault(require("events"));
18
+ const resource_class_1 = __importDefault(require("./resource.class"));
19
+ const s3_client_class_1 = __importDefault(require("./s3-client.class"));
20
+ const validator_1 = require("./validator");
21
+ const errors_1 = require("./errors");
22
+ class S3db extends events_1.default {
23
+ /**
24
+ * Constructor
25
+ */
26
+ constructor(options) {
27
+ super();
28
+ this.keyPrefix = "";
29
+ this.bucket = "s3db";
30
+ this.cache = false;
31
+ this.version = "1";
32
+ this.resources = {};
33
+ this.options = options;
34
+ this.parallelism = parseInt(options.parallelism + "") || 10;
35
+ this.plugins = options.plugins || [];
36
+ this.cache = options.cache;
37
+ this.passphrase = options.passphrase || "";
38
+ this.validatorInstance = (0, validator_1.ValidatorFactory)({
39
+ passphrase: options === null || options === void 0 ? void 0 : options.passphrase,
40
+ });
41
+ this.client = new s3_client_class_1.default({
42
+ connectionString: options.uri,
43
+ parallelism: this.parallelism,
44
+ });
45
+ this.bucket = this.client.bucket;
46
+ this.keyPrefix = this.client.keyPrefix;
47
+ this.startPlugins();
48
+ }
49
+ /**
50
+ * Remotely setups s3db file.
51
+ */
52
+ connect() {
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ let metadata = null;
55
+ try {
56
+ metadata = yield this.getMetadataFile();
57
+ }
58
+ catch (error) {
59
+ if (error instanceof errors_1.S3dbMissingMetadata) {
60
+ metadata = this.blankMetadataStructure();
61
+ yield this.uploadMetadataFile();
62
+ }
63
+ else {
64
+ this.emit("error", error);
65
+ return Promise.reject(error);
66
+ }
67
+ }
68
+ for (const resource of Object.entries(metadata.resources)) {
69
+ const [name, definition] = resource;
70
+ this.resources[name] = new resource_class_1.default({
71
+ name,
72
+ s3db: this,
73
+ s3Client: this.client,
74
+ schema: definition.schema,
75
+ options: definition.options,
76
+ validatorInstance: this.validatorInstance,
77
+ });
78
+ }
79
+ this.emit("connected", new Date());
80
+ });
81
+ }
82
+ startPlugins() {
83
+ return __awaiter(this, void 0, void 0, function* () {
84
+ if (this.plugins && !(0, lodash_1.isEmpty)(this.plugins)) {
85
+ const startProms = this.plugins.map((plugin) => plugin.setup(this));
86
+ yield Promise.all(startProms);
87
+ this.plugins.map((plugin) => plugin.start());
88
+ }
89
+ });
90
+ }
91
+ /**
92
+ * Downloads current metadata.
93
+ * If there isnt any file, creates an empty metadata.
94
+ * @returns MetadataInterface
95
+ */
96
+ getMetadataFile() {
97
+ return __awaiter(this, void 0, void 0, function* () {
98
+ try {
99
+ const request = yield this.client.getObject({ key: `s3db.json` });
100
+ const metadata = JSON.parse(String(request === null || request === void 0 ? void 0 : request.Body));
101
+ return this.unserializeMetadata(metadata);
102
+ }
103
+ catch (error) {
104
+ if (error instanceof errors_1.ClientNoSuchKey) {
105
+ return Promise.reject(new errors_1.S3dbMissingMetadata({ bucket: this.bucket, cause: error }));
106
+ }
107
+ else {
108
+ return Promise.reject(error);
109
+ }
110
+ }
111
+ });
112
+ }
113
+ unserializeMetadata(metadata) {
114
+ const file = Object.assign({}, metadata);
115
+ if ((0, lodash_1.isEmpty)(file.resources))
116
+ return file;
117
+ for (const [name, structure] of Object.entries(file.resources)) {
118
+ for (const [attr, value] of Object.entries(structure.schema)) {
119
+ file.resources[name].schema[attr] = JSON.parse(value);
120
+ }
121
+ }
122
+ return file;
123
+ }
124
+ uploadMetadataFile() {
125
+ return __awaiter(this, void 0, void 0, function* () {
126
+ const file = {
127
+ version: this.version,
128
+ resources: Object.entries(this.resources).reduce((acc, definition) => {
129
+ const [name, resource] = definition;
130
+ acc[name] = resource.export();
131
+ return acc;
132
+ }, {}),
133
+ };
134
+ yield this.client.putObject({
135
+ key: `s3db.json`,
136
+ body: JSON.stringify(file, null, 2),
137
+ });
138
+ });
139
+ }
140
+ /**
141
+ * Generates empty metadata structure.
142
+ * @returns MetadataInterface
143
+ */
144
+ blankMetadataStructure() {
145
+ return {
146
+ version: `1`,
147
+ resources: {},
148
+ };
149
+ }
150
+ /**
151
+ * Generates a new resorce with its translators and validatos.
152
+ * @param {Object} param
153
+ * @param {string} param.resourceName
154
+ * @param {Object} param.attributes
155
+ * @param {Object} param.options
156
+ */
157
+ createResource({ resourceName, attributes, options = {}, }) {
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ const schema = (0, flat_1.flatten)(attributes, { safe: true });
160
+ const resource = new resource_class_1.default({
161
+ schema,
162
+ s3db: this,
163
+ name: resourceName,
164
+ s3Client: this.client,
165
+ validatorInstance: this.validatorInstance,
166
+ options: Object.assign({ autoDecrypt: true, cache: this.cache }, options),
167
+ });
168
+ this.resources[resourceName] = resource;
169
+ yield this.uploadMetadataFile();
170
+ return resource;
171
+ });
172
+ }
173
+ /**
174
+ * Looper
175
+ * @param {string} resourceName
176
+ * @returns
177
+ */
178
+ resource(resourceName) {
179
+ if (!this.resources[resourceName]) {
180
+ return Promise.reject(`resource ${resourceName} does not exist`);
181
+ }
182
+ return this.resources[resourceName];
183
+ }
184
+ }
185
+ exports.default = S3db;
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ const path = __importStar(require("path"));
36
+ const lodash_1 = require("lodash");
37
+ const node_stream_1 = require("node:stream");
38
+ const promise_pool_1 = require("@supercharge/promise-pool");
39
+ class ResourceIdsReadStream extends node_stream_1.Readable {
40
+ constructor({ resource }) {
41
+ super({
42
+ objectMode: true,
43
+ highWaterMark: resource.s3Client.parallelism * 3,
44
+ });
45
+ this.resource = resource;
46
+ this.pagesCount = 0;
47
+ this.content = [];
48
+ this.finishedReadingResource = false;
49
+ this.loading = this.getItems();
50
+ }
51
+ _read(size) {
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ if (this.content.length === 0) {
54
+ if (this.loading) {
55
+ yield this.loading;
56
+ }
57
+ else if (this.finishedReadingResource) {
58
+ this.push(null);
59
+ return;
60
+ }
61
+ }
62
+ const data = this.content.shift();
63
+ this.push(data);
64
+ });
65
+ }
66
+ getItems({ continuationToken = null, } = {}) {
67
+ return __awaiter(this, void 0, void 0, function* () {
68
+ this.emit("page", this.pagesCount++);
69
+ const res = yield this.resource.s3Client.listObjects({
70
+ prefix: `resource=${this.resource.name}`,
71
+ continuationToken,
72
+ });
73
+ if (res.Contents) {
74
+ const contents = (0, lodash_1.chunk)(res.Contents, this.resource.s3Client.parallelism);
75
+ yield promise_pool_1.PromisePool.for(contents)
76
+ .withConcurrency(5)
77
+ .handleError((error, content) => __awaiter(this, void 0, void 0, function* () {
78
+ this.emit("error", error, content);
79
+ }))
80
+ .process((pkg) => {
81
+ const ids = pkg.map((obj) => {
82
+ return (obj.Key || "").replace(path.join(this.resource.s3Client.keyPrefix, `resource=${this.resource.name}`, "id="), "");
83
+ });
84
+ this.content.push(ids);
85
+ ids.forEach((id) => this.emit("id", this.resource.name, id));
86
+ });
87
+ }
88
+ this.finishedReadingResource = !res.IsTruncated;
89
+ if (res.NextContinuationToken) {
90
+ this.loading = this.getItems({
91
+ continuationToken: res.NextContinuationToken,
92
+ });
93
+ }
94
+ else {
95
+ this.loading = null;
96
+ }
97
+ });
98
+ }
99
+ }
100
+ exports.default = ResourceIdsReadStream;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const lodash_1 = require("lodash");
13
+ const promise_pool_1 = require("@supercharge/promise-pool");
14
+ const node_stream_1 = require("node:stream");
15
+ class ResourceIdsToDataTransformer extends node_stream_1.Transform {
16
+ constructor({ resource }) {
17
+ super({ objectMode: true, highWaterMark: resource.s3Client.parallelism * 2 });
18
+ this.resource = resource;
19
+ }
20
+ _transform(chunk, encoding, callback) {
21
+ return __awaiter(this, void 0, void 0, function* () {
22
+ if (!(0, lodash_1.isArray)(chunk))
23
+ this.push(null);
24
+ this.emit("page", chunk);
25
+ yield promise_pool_1.PromisePool.for(chunk)
26
+ .withConcurrency(this.resource.s3Client.parallelism)
27
+ .handleError((error, content) => __awaiter(this, void 0, void 0, function* () {
28
+ this.emit("error", error, content);
29
+ }))
30
+ .process((id) => __awaiter(this, void 0, void 0, function* () {
31
+ this.emit("id", id);
32
+ const data = yield this.resource.getById(id);
33
+ this.push(data);
34
+ return data;
35
+ }));
36
+ callback(null);
37
+ });
38
+ }
39
+ }
40
+ exports.default = ResourceIdsToDataTransformer;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const lodash_1 = require("lodash");
13
+ const node_stream_1 = require("node:stream");
14
+ class ResourceWriteStream extends node_stream_1.Writable {
15
+ constructor({ resource }) {
16
+ super({ objectMode: true, highWaterMark: resource.s3Client.parallelism * 2 });
17
+ this.resource = resource;
18
+ this.contents = [];
19
+ this.running = null;
20
+ this.receivedFinalMessage = false;
21
+ }
22
+ _write(chunk, encoding, callback) {
23
+ return __awaiter(this, void 0, void 0, function* () {
24
+ if (this.running)
25
+ yield this.running;
26
+ if (!(0, lodash_1.isEmpty)(chunk)) {
27
+ this.contents.push(chunk);
28
+ }
29
+ else {
30
+ this.receivedFinalMessage = true;
31
+ }
32
+ this.running = this.writeOrWait();
33
+ return callback(null);
34
+ });
35
+ }
36
+ _writev(chunks, callback) {
37
+ return __awaiter(this, void 0, void 0, function* () {
38
+ if (this.running)
39
+ yield this.running;
40
+ if (!(0, lodash_1.isEmpty)(chunks)) {
41
+ for (const obj of chunks.map((c) => c.chunk)) {
42
+ this.contents.push(obj);
43
+ }
44
+ }
45
+ else {
46
+ this.receivedFinalMessage = true;
47
+ }
48
+ this.running = this.writeOrWait();
49
+ return callback(null);
50
+ });
51
+ }
52
+ writeOrWait() {
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ if (this.receivedFinalMessage) {
55
+ const data = this.contents.splice(0, this.contents.length - 1);
56
+ yield this.resource.bulkInsert(data);
57
+ this.emit("end");
58
+ return;
59
+ }
60
+ if (this.contents.length < this.resource.s3Client.parallelism)
61
+ return;
62
+ const objs = this.contents.splice(0, this.resource.s3Client.parallelism);
63
+ objs.forEach((obj) => this.emit("id", obj.id));
64
+ yield this.resource.bulkInsert(objs);
65
+ objs.forEach((obj) => this.emit("data", obj));
66
+ });
67
+ }
68
+ _final(callback) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ this.receivedFinalMessage = true;
71
+ yield this.writeOrWait();
72
+ callback(null);
73
+ });
74
+ }
75
+ }
76
+ exports.default = ResourceWriteStream;