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,16 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AvroSerializer = exports.CacheAvroSchema = void 0;
7
+ const avsc_1 = __importDefault(require("avsc"));
8
+ exports.CacheAvroSchema = avsc_1.default.Type.forSchema({
9
+ name: "Cache",
10
+ type: "record",
11
+ fields: [{ name: "data", type: ["string"] }],
12
+ });
13
+ exports.AvroSerializer = {
14
+ serialize: (data) => String(exports.CacheAvroSchema.toBuffer(data)),
15
+ unserialize: (data) => exports.CacheAvroSchema.fromBuffer(Buffer.from(data)),
16
+ };
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JsonSerializer = void 0;
4
+ exports.JsonSerializer = {
5
+ serialize: (data) => JSON.stringify(data),
6
+ unserialize: (data) => JSON.parse(data),
7
+ };
@@ -0,0 +1,157 @@
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 zlib_1 = __importDefault(require("zlib"));
39
+ const path = __importStar(require("path"));
40
+ const lodash_1 = require("lodash");
41
+ const serializers_type_1 = __importDefault(require("./serializers.type"));
42
+ const json_serializer_1 = require("./json.serializer");
43
+ const avro_serializer_1 = require("./avro.serializer");
44
+ class S3Cache {
45
+ constructor({ s3Client, compressData = true, serializer = serializers_type_1.default.json, }) {
46
+ this.s3Client = s3Client;
47
+ this.serializer = serializer;
48
+ this.compressData = compressData;
49
+ this.serializers = {
50
+ [serializers_type_1.default.json]: json_serializer_1.JsonSerializer,
51
+ [serializers_type_1.default.avro]: avro_serializer_1.AvroSerializer,
52
+ };
53
+ }
54
+ getKey({ params, hashed = true, additionalPrefix = "", }) {
55
+ let filename = Object.keys(params || {})
56
+ .sort()
57
+ .map((x) => `${x}:${params[x]}`)
58
+ .join("|");
59
+ if (filename.length === 0)
60
+ filename = `empty`;
61
+ if (additionalPrefix.length > 0) {
62
+ filename = additionalPrefix + `|` + filename;
63
+ }
64
+ if (hashed) {
65
+ filename = Buffer.from(filename)
66
+ .toString("base64")
67
+ .split("")
68
+ .reverse()
69
+ .join("");
70
+ }
71
+ filename = filename + "." + this.serializer;
72
+ if (this.compressData)
73
+ filename += ".gz";
74
+ return path.join("cache", filename);
75
+ }
76
+ _put({ key, data }) {
77
+ return __awaiter(this, void 0, void 0, function* () {
78
+ const lengthRaw = (0, lodash_1.isString)(data)
79
+ ? data.length
80
+ : JSON.stringify(data).length;
81
+ let body = this.serialize({ data });
82
+ const lengthSerialized = body.length;
83
+ if (this.compressData) {
84
+ body = zlib_1.default.gzipSync(body);
85
+ }
86
+ const metadata = {
87
+ compressor: "zlib",
88
+ "client-id": this.s3Client.id,
89
+ serializer: String(this.serializer),
90
+ compressed: String(this.compressData),
91
+ "length-raw": String(lengthRaw),
92
+ "length-serialized": String(lengthSerialized),
93
+ "length-compressed": String(body.length),
94
+ };
95
+ return this.s3Client.putObject({
96
+ key,
97
+ body,
98
+ metadata,
99
+ contentEncoding: this.compressData ? "gzip" : null,
100
+ contentType: this.compressData
101
+ ? "application/gzip"
102
+ : `application/${this.serializer}`,
103
+ });
104
+ });
105
+ }
106
+ _get({ key }) {
107
+ return __awaiter(this, void 0, void 0, function* () {
108
+ try {
109
+ const res = yield this.s3Client.getObject({ key });
110
+ if (!res.Body)
111
+ return "";
112
+ let content = res.Body;
113
+ if (res.Metadata) {
114
+ const { serializer, compressor, compressed } = res.Metadata;
115
+ if (["true", true].includes(compressed)) {
116
+ if (compressor === `zlib`) {
117
+ content = zlib_1.default.unzipSync(content);
118
+ }
119
+ }
120
+ const { data } = this.serializers[serializer].unserialize(content);
121
+ return data;
122
+ }
123
+ return this.unserialize(content);
124
+ }
125
+ catch (error) {
126
+ if (error instanceof Error) {
127
+ if (error.name !== "ClientNoSuchKey") {
128
+ return Promise.reject(error);
129
+ }
130
+ }
131
+ }
132
+ return null;
133
+ });
134
+ }
135
+ _delete({ key }) {
136
+ return __awaiter(this, void 0, void 0, function* () {
137
+ try {
138
+ yield this.s3Client.deleteObject(key);
139
+ }
140
+ catch (error) {
141
+ if (error instanceof Error) {
142
+ if (error.name !== "ClientNoSuchKey") {
143
+ return Promise.reject(error);
144
+ }
145
+ }
146
+ }
147
+ return true;
148
+ });
149
+ }
150
+ serialize(data) {
151
+ return this.serializers[this.serializer].serialize(data);
152
+ }
153
+ unserialize(data) {
154
+ return this.serializers[this.serializer].unserialize(data);
155
+ }
156
+ }
157
+ exports.default = S3Cache;
@@ -0,0 +1,77 @@
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 s3_cache_class_1 = __importDefault(require("./s3-cache.class"));
16
+ const serializers_type_1 = __importDefault(require("./serializers.type"));
17
+ class S3ResourceCache extends s3_cache_class_1.default {
18
+ constructor({ resource, compressData = true, serializer = serializers_type_1.default.json, }) {
19
+ super({
20
+ s3Client: resource.s3Client,
21
+ compressData: compressData,
22
+ serializer: serializer,
23
+ });
24
+ this.resource = resource;
25
+ }
26
+ getKey({ action = "list", params }) {
27
+ return super.getKey({
28
+ params,
29
+ additionalPrefix: `${this.resource.name}|${action}`,
30
+ });
31
+ }
32
+ put({ action = "list", params, data, }) {
33
+ const _super = Object.create(null, {
34
+ _put: { get: () => super._put }
35
+ });
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ return _super._put.call(this, {
38
+ data,
39
+ key: this.getKey({ action, params }),
40
+ });
41
+ });
42
+ }
43
+ get({ action = "list", params }) {
44
+ const _super = Object.create(null, {
45
+ _get: { get: () => super._get }
46
+ });
47
+ return __awaiter(this, void 0, void 0, function* () {
48
+ return _super._get.call(this, {
49
+ key: this.getKey({ action, params }),
50
+ });
51
+ });
52
+ }
53
+ delete({ action = "list", params }) {
54
+ const _super = Object.create(null, {
55
+ _delete: { get: () => super._delete }
56
+ });
57
+ return __awaiter(this, void 0, void 0, function* () {
58
+ const key = this.getKey({ action, params });
59
+ return _super._delete.call(this, {
60
+ key: this.getKey({ action, params }),
61
+ });
62
+ });
63
+ }
64
+ purge() {
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ let keys = yield this.s3Client.getAllKeys({ prefix: "cache" });
67
+ const key = Buffer.from(this.resource.name)
68
+ .toString("base64")
69
+ .split("")
70
+ .reverse()
71
+ .join("");
72
+ keys = keys.filter((k) => k.includes(key));
73
+ yield this.s3Client.deleteObjects(keys);
74
+ });
75
+ }
76
+ }
77
+ exports.default = S3ResourceCache;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Serializers = void 0;
4
+ exports.Serializers = {
5
+ json: "json",
6
+ avro: "avro",
7
+ };
8
+ exports.default = exports.Serializers;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.S3dbInvalidResource = exports.S3dbMissingMetadata = exports.BaseS3dbError = exports.ClientNoSuchKey = exports.BaseS3Error = exports.BaseError = void 0;
4
+ class BaseError extends Error {
5
+ constructor({ bucket, message, cause }) {
6
+ super(message);
7
+ if (typeof Error.captureStackTrace === 'function') {
8
+ Error.captureStackTrace(this, this.constructor);
9
+ }
10
+ else {
11
+ this.stack = (new Error(message)).stack;
12
+ }
13
+ super.name = this.constructor.name;
14
+ this.name = this.constructor.name;
15
+ this.cause = cause;
16
+ this.thrownAt = new Date();
17
+ }
18
+ toJson() {
19
+ return Object.assign({}, this);
20
+ }
21
+ toString() {
22
+ return `${this.name} | ${this.message}`;
23
+ }
24
+ }
25
+ exports.BaseError = BaseError;
26
+ // AWS S3 errors
27
+ class BaseS3Error extends BaseError {
28
+ constructor({ bucket, message }) {
29
+ super({ bucket, message });
30
+ }
31
+ }
32
+ exports.BaseS3Error = BaseS3Error;
33
+ class ClientNoSuchKey extends BaseS3Error {
34
+ constructor({ bucket, key }) {
35
+ super({ bucket, message: `Key does not exists [s3://${bucket}/${key}]` });
36
+ this.key = key;
37
+ }
38
+ }
39
+ exports.ClientNoSuchKey = ClientNoSuchKey;
40
+ // Our errors
41
+ class BaseS3dbError extends BaseError {
42
+ constructor({ bucket, message, cause }) {
43
+ super({ bucket, message, cause });
44
+ }
45
+ }
46
+ exports.BaseS3dbError = BaseS3dbError;
47
+ class S3dbMissingMetadata extends BaseS3dbError {
48
+ constructor({ bucket, cause }) {
49
+ super({ bucket, cause, message: `Missing metadata for bucket [s3://${bucket}]` });
50
+ }
51
+ }
52
+ exports.S3dbMissingMetadata = S3dbMissingMetadata;
53
+ class S3dbInvalidResource extends BaseS3dbError {
54
+ constructor({ bucket, resourceName, attributes, validation, }) {
55
+ super({
56
+ bucket,
57
+ message: `Resource is not valid. Name=${resourceName} [s3://${bucket}].\n${JSON.stringify(validation, null, 2)}`,
58
+ });
59
+ this.resourceName = resourceName;
60
+ this.attributes = attributes;
61
+ this.validation = validation;
62
+ }
63
+ }
64
+ exports.S3dbInvalidResource = S3dbInvalidResource;
package/build/index.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.S3db = void 0;
7
+ const s3db_class_1 = __importDefault(require("./s3db.class"));
8
+ exports.S3db = s3db_class_1.default;
9
+ exports.default = s3db_class_1.default;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });