diff --git a/.gitignore b/.gitignore index b512c09..f06235c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules \ No newline at end of file +node_modules +dist diff --git a/dist/index.d.ts b/dist/index.d.ts index 6970caa..8e62293 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1 +1,3 @@ -export declare const helloWorld: () => string; +export * from "./hasura-storage-api"; +export * from "./hasura-storage-client"; +export * from "./utils/types"; diff --git a/dist/index.js b/dist/index.js index 24527dc..edb7b34 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,8 +1,4 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.helloWorld = void 0; -var helloWorld = function () { - return 'Howdy!'; -}; -exports.helloWorld = helloWorld; +export * from "./hasura-storage-api"; +export * from "./hasura-storage-client"; +export * from "./utils/types"; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map index 61ea652..c6492f8 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAO,IAAM,UAAU,GAAG;IACtB,OAAO,QAAQ,CAAA;AACnB,CAAC,CAAA;AAFY,QAAA,UAAU,cAEtB"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1d42f38..4e69428 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,31 +1,31 @@ { - "name": "typescript-package-starter", - "version": "1.1.0", + "name": "@suplere/hbp-storage-js", + "version": "0.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", - "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", "dev": true, "requires": { - "@babel/highlight": "^7.16.0" + "@babel/highlight": "^7.16.7" } }, "@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", "dev": true }, "@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.15.7", + "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -128,21 +128,21 @@ "dev": true }, "@types/chai": { - "version": "4.2.22", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.22.tgz", - "integrity": "sha512-tFfcE+DSTzWAgifkjik9AySNqIyNoYwmR+uecPwwD/XRNfvOjmC/FjCxpiUGDkDVDphPfCUecSQVFw+lN3M3kQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", + "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==", "dev": true }, "@types/mocha": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", - "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", + "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", "dev": true }, "@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==", + "version": "16.11.22", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.22.tgz", + "integrity": "sha512-DYNtJWauMQ9RNpesl4aVothr97/tIJM8HbyOXJ0AYT1Z2bEjLHyfjOBPAQQVMLf8h3kSShYfNk8Wnto8B2zHUA==", "dev": true }, "@ungap/promise-all-settled": { @@ -152,9 +152,9 @@ "dev": true }, "acorn": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", - "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true }, "acorn-walk": { @@ -212,6 +212,14 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, + "axios": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", + "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "requires": { + "follow-redirects": "^1.14.7" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -256,21 +264,22 @@ "dev": true }, "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "dev": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", + "loupe": "^2.3.1", "pathval": "^1.1.1", "type-detect": "^4.0.5" } @@ -303,9 +312,9 @@ "dev": true }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -363,9 +372,9 @@ "dev": true }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { "ms": "2.1.2" @@ -449,6 +458,11 @@ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, + "follow-redirects": { + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -481,9 +495,9 @@ "dev": true }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -556,9 +570,9 @@ } }, "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "requires": { "has": "^1.0.3" @@ -643,6 +657,15 @@ "is-unicode-supported": "^0.1.0" } }, + "loupe": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.3.tgz", + "integrity": "sha512-krIV4Cf1BIGIx2t1e6tucThhrBemUnIUjMtD2vN4mrMxnxpBvrcosBSpooqunBqP/hOEEV1w/Cr1YskGtqw5Jg==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -674,32 +697,32 @@ } }, "mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz", + "integrity": "sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.2.0", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -712,9 +735,9 @@ "dev": true }, "nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", + "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "dev": true }, "normalize-path": { @@ -775,9 +798,9 @@ "dev": true }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "randombytes": { @@ -805,13 +828,14 @@ "dev": true }, "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "safe-buffer": { @@ -876,6 +900,12 @@ "has-flag": "^4.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -886,9 +916,9 @@ } }, "ts-node": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", - "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.5.0.tgz", + "integrity": "sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.7.0", @@ -902,6 +932,7 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.0", "yn": "3.1.1" }, "dependencies": { @@ -1044,6 +1075,12 @@ "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", "dev": true }, + "v8-compile-cache-lib": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", + "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "dev": true + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1054,9 +1091,9 @@ } }, "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { diff --git a/package.json b/package.json index 9860e54..2e9e2c3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "@suplere/typescript-package-starter", - "version": "1.1.1", - "description": "TypeScript boilerplate for NPM or Github Packages", + "name": "@suplere/hbp-storage-js", + "version": "0.0.1", + "description": "Implement for hasura backend plus - storage part. With multi app support", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { @@ -19,19 +19,21 @@ "license": "ISC", "repository": { "type": "git", - "url": "git+https://github.com/suplere/typescript-package-starter.git" + "url": "git+https://github.com/suplere/hbp-storage-js.git" }, "devDependencies": { - "@types/chai": "^4.2.22", - "@types/mocha": "^9.0.0", - "@types/node": "^16.10.2", - "chai": "^4.3.4", - "mocha": "^9.1.2", + "@types/chai": "^4.3.0", + "@types/mocha": "^9.1.0", + "@types/node": "^16.11.22", + "chai": "^4.3.6", + "mocha": "^9.2.0", + "ts-node": "^10.5.0", "tslint": "^6.1.3", - "ts-node": "^10.2.1", - "typescript": "~4.4.3" + "typescript": "^4.4.4" + }, + "dependencies": { + "axios": "^0.25.0" }, - "dependencies": {}, "engines": { "node": ">=8" }, diff --git a/readme.md b/readme.md index 1842d0b..13600b5 100644 --- a/readme.md +++ b/readme.md @@ -1,21 +1,2 @@ -# TypeScript Package Starter +# HASURA BACKED PLUS - storage -TypeScript boilerplate for NPM or Github Packages - -## Build project - -```sh -npm run build -``` - -## Test pacakge - -```sh -npm test -``` - -## Deploy package - -```sh -npm run deploy -``` \ No newline at end of file diff --git a/src/hasura-storage-api.ts b/src/hasura-storage-api.ts new file mode 100644 index 0000000..c0e4196 --- /dev/null +++ b/src/hasura-storage-api.ts @@ -0,0 +1,150 @@ +import axios, { AxiosInstance } from "axios"; +import { + ApiDeleteParams, + ApiDeleteResponse, + ApiGetPresignedUrlParams, + ApiGetPresignedUrlResponse, + ApiUploadParams, + ApiUploadResponse, + StorageMetadataResponse, + UploadHeaders, +} from "./utils/types"; + +export class HasuraStorageApi { + private url: string; + private httpClient: AxiosInstance; + private accessToken: string | undefined; + private appId: string | null; + + constructor({ url, appId }: { url: string; appId: string | null }) { + this.url = url; + this.appId = appId; + + this.httpClient = axios.create({ + baseURL: this.appId + ? `${this.url}/custom/storage/${this.appId}` + : this.url, + timeout: 10000, + }); + } + + public async upload(params: ApiUploadParams): Promise { + try { + const res = await this.httpClient.post("/files", params.file, { + headers: { + ...this.generateUploadHeaders(params), + ...this.generateAuthHeaders(), + }, + }); + + return { fileMetadata: res.data, error: null }; + } catch (error) { + return { fileMetadata: null, error: error as Error }; + } + } + + public async getPresignedUrl( + params: ApiGetPresignedUrlParams + ): Promise { + try { + const { fileId } = params; + const res = await this.httpClient.get(`/files/${fileId}/presignedurl`, { + headers: { + ...this.generateAuthHeaders(), + }, + }); + return { presignedUrl: res.data, error: null }; + } catch (error) { + return { presignedUrl: null, error: error as Error }; + } + } + + public async delete(params: ApiDeleteParams): Promise { + try { + const { fileId } = params; + await this.httpClient.delete(`/files/${fileId}`, { + headers: { + ...this.generateAuthHeaders(), + }, + }); + return { error: null }; + } catch (error) { + return { error: error as Error }; + } + } + + public setAccessToken(accessToken: string | undefined) { + this.accessToken = accessToken; + } + + private generateUploadHeaders(params: ApiUploadParams): UploadHeaders { + const { bucketId, name, id } = params; + let uploadheaders: UploadHeaders = {}; + + if (bucketId) { + uploadheaders["x-nhost-bucket-id"] = bucketId; + } + if (id) { + uploadheaders["x-nhost-file-id"] = id; + } + if (name) { + uploadheaders["x-nhost-file-name"] = name; + } + return uploadheaders; + } + + private generateAuthHeaders() { + if (!this.accessToken) { + return null; + } + + return { + Authorization: `Bearer ${this.accessToken}`, + }; + } + + public async uploadFile({ + path, + formData, + onUploadProgress = undefined, + }: { + path: string; + formData: FormData; + onUploadProgress: any | undefined; + }): Promise { + try { + const res = await this.httpClient.post(`/o${path}`, formData, { + headers: { + "Content-Type": "multipart/form-data", + ...this.generateAuthHeaders(), + }, + onUploadProgress, + }); + return { fileMetadata: res.data, error: null }; + } catch (error) { + return { fileMetadata: null, error: error as Error }; + } + } + + async deleteFile(path: string): Promise { + try { + await this.httpClient.delete(`/o${path}`, { + headers: { + ...this.generateAuthHeaders(), + }, + }); + return { error: null }; + } catch (error) { + return { error: error as Error }; + } + } + + async getMetadata(path: string): Promise { + const res = await this.httpClient.get(`/m${path}`, { + headers: { + ...this.generateAuthHeaders(), + }, + }); + return res.data; + } +} diff --git a/src/hasura-storage-client.ts b/src/hasura-storage-client.ts new file mode 100644 index 0000000..313b24e --- /dev/null +++ b/src/hasura-storage-client.ts @@ -0,0 +1,229 @@ +import { HasuraStorageApi } from "./hasura-storage-api"; +import { base64Bytes, percentEncodedBytes, StringFormat, utf8Bytes } from "./utils"; +import { + StorageDeleteParams, + StorageDeleteResponse, + StorageGetPresignedUrlParams, + StorageGetPresignedUrlResponse, + StorageGetUrlParams, + StorageMetadataResponse, + StorageUploadFile, + StorageUploadParams, + StorageUploadResponse, + StorageUploadString, +} from "./utils/types"; + +export class HasuraStorageClient { + private url: string; + private api: HasuraStorageApi; + private appId: string | null; + + constructor({ url, appId = null }: { url: string; appId?: string | null }) { + this.url = url; + this.appId = appId; + this.api = new HasuraStorageApi({ url, appId }); + } + + /** + * + * Use `.upload` to upload a file. + * + * @example + * + * storage.upload({ file }) + * + */ + public async upload( + params: StorageUploadParams + ): Promise { + let file = new FormData(); + file.append("file", params.file); + + const { fileMetadata, error } = await this.api.upload({ + ...params, + file, + }); + if (error) { + return { fileMetadata: null, error }; + } + + if (!fileMetadata) { + return { fileMetadata: null, error: new Error("Invalid file returned") }; + } + + return { fileMetadata, error: null }; + } + + /** + * + * Use `.getUrl` to direct file URL to a file. + * + * @example + * + * storage.getUrl({ fileId: 'uuid' }) + * + */ + public getUrl(params: StorageGetUrlParams): string { + const { fileId } = params; + return this.appId + ? `/custom/storage/${this.appId}/o/${fileId}` + : `${this.url}/files/${fileId}`; + } + + /** + * + * Use `.getPresignedUrl` to get a presigned URL to a file. + * + * @example + * + * storage.getPresignedUrl({ fileId: 'uuid' }) + * + * + */ + public async getPresignedUrl( + params: StorageGetPresignedUrlParams + ): Promise { + const { presignedUrl, error } = await this.api.getPresignedUrl(params); + if (error) { + return { presignedUrl: null, error }; + } + + if (!presignedUrl) { + return { presignedUrl: null, error: new Error("Invalid file id") }; + } + + return { presignedUrl, error: null }; + } + + /** + * + * Use `.delete` to delete a file. + * + * @example + * + * storage.delete({ fileId: 'uuid' }) + * + * + */ + public async delete( + params: StorageDeleteParams + ): Promise { + const { error } = await this.api.delete(params); + if (error) { + return { error }; + } + + return { error: null }; + } + + public async uploadFileToStorage( + params: StorageUploadFile + ): Promise { + if (!params.path.startsWith("/")) { + throw new Error("`path` must start with `/`"); + } + let formData = new FormData(); + formData.append("file", params.file); + const { fileMetadata, error } = await this.api.uploadFile({ + path: params.path, + formData, + onUploadProgress: params.onUploadProgress || undefined, + }); + if (error) { + return { fileMetadata: null, error }; + } + + if (!fileMetadata) { + return { fileMetadata: null, error: new Error("Invalid file returned") }; + } + + return { fileMetadata, error: null }; + } + + public async uploadStringToStorage( + params: StorageUploadString + ): Promise { + if (!params.path.startsWith("/")) { + throw new Error("`path` must start with `/`"); + } + + let fileData; + let contentType: string | undefined; + if (!params.type) params.type = "raw"; + if (params.type === "raw") { + fileData = utf8Bytes(params.data); + contentType = + params.metadata && params.metadata.hasOwnProperty("content-type") + ? params.metadata["content-type"] + : undefined; + } else if (params.type === "data_url") { + let isBase64 = false; + const matches = params.data.match(/^data:([^,]+)?,/); + if (matches === null) { + throw "Data must be formatted 'data:[][;base64],"; + } + const middle = matches[1] || null; + if (middle != null) { + isBase64 = middle.endsWith(";base64"); + contentType = isBase64 + ? middle.substring(0, middle.length - ";base64".length) + : middle; + } + const restData = params.data.substring(params.data.indexOf(",") + 1); + fileData = isBase64 + ? base64Bytes(StringFormat.BASE64, restData) + : percentEncodedBytes(restData); + } + if (!fileData) { + throw new Error("Unbale to generate file data"); + } + + const file = new File([fileData], "untitled", { type: contentType }); + // create form data + let formData = new FormData(); + formData.append("file", file); + + const { fileMetadata, error } = await this.api.uploadFile({ + path: params.path, + formData, + onUploadProgress: params.onUploadProgress || undefined, + }); + if (error) { + return { fileMetadata: null, error }; + } + + if (!fileMetadata) { + return { fileMetadata: null, error: new Error("Invalid file returned") }; + } + + return { fileMetadata, error: null }; + } + + public async deleteFile(path: string): Promise { + if (!path.startsWith("/")) { + return { error: new Error("`path` must start with `/`") }; + } + const { error } = await this.api.deleteFile(path); + if (error) { + return { error }; + } + + return { error: null }; + } + + public async getS3Metadata(path: string): Promise { + if (!path.startsWith("/")) { + return { metadata: null, error: new Error("`path` must start with `/`") }; + } + const { metadata, error } = await this.api.getMetadata(path); + if (error) { + return { metadata:null, error }; + } + + return { metadata, error: null }; + } + + public setAccessToken(accessToken: string | undefined): void { + this.api.setAccessToken(accessToken); + } +} diff --git a/src/index.ts b/src/index.ts index fd20d27..8e62293 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ -export const helloWorld = () => { - return 'Hello from pakage!' -} \ No newline at end of file +export * from "./hasura-storage-api"; +export * from "./hasura-storage-client"; +export * from "./utils/types"; diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..9555996 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,96 @@ +export type StringFormat = string; +export const StringFormat = { + RAW: "raw", + BASE64: "base64", + BASE64URL: "base64url", + DATA_URL: "data_url", +}; + +export function utf8Bytes(value: string): Uint8Array { + const b: number[] = []; + for (let i = 0; i < value.length; i++) { + let c = value.charCodeAt(i); + if (c <= 127) { + b.push(c); + } else { + if (c <= 2047) { + b.push(192 | (c >> 6), 128 | (c & 63)); + } else { + if ((c & 64512) === 55296) { + // The start of a surrogate pair. + const valid = + i < value.length - 1 && (value.charCodeAt(i + 1) & 64512) === 56320; + if (!valid) { + // The second surrogate wasn't there. + b.push(239, 191, 189); + } else { + const hi = c; + const lo = value.charCodeAt(++i); + c = 65536 | ((hi & 1023) << 10) | (lo & 1023); + b.push( + 240 | (c >> 18), + 128 | ((c >> 12) & 63), + 128 | ((c >> 6) & 63), + 128 | (c & 63) + ); + } + } else { + if ((c & 64512) === 56320) { + // Invalid low surrogate. + b.push(239, 191, 189); + } else { + b.push(224 | (c >> 12), 128 | ((c >> 6) & 63), 128 | (c & 63)); + } + } + } + } + } + return new Uint8Array(b); +} + +export function base64Bytes(format: StringFormat, value: string): Uint8Array { + switch (format) { + case StringFormat.BASE64: { + const hasMinus = value.indexOf("-") !== -1; + const hasUnder = value.indexOf("_") !== -1; + if (hasMinus || hasUnder) { + const invalidChar = hasMinus ? "-" : "_"; + throw `Invalid character '${invalidChar}' found: is it base64url encoded?`; + } + break; + } + case StringFormat.BASE64URL: { + const hasPlus = value.indexOf("+") !== -1; + const hasSlash = value.indexOf("/") !== -1; + if (hasPlus || hasSlash) { + const invalidChar = hasPlus ? "+" : "/"; + throw `Invalid character '${invalidChar}' found: is it base64url encoded?`; + } + value = value.replace(/-/g, "+").replace(/_/g, "/"); + break; + } + default: + // do nothing + } + let bytes; + try { + bytes = atob(value); + } catch (e) { + throw `Invalid character found`; + } + const array = new Uint8Array(bytes.length); + for (let i = 0; i < bytes.length; i++) { + array[i] = bytes.charCodeAt(i); + } + return array; +} + +export function percentEncodedBytes(value: string): Uint8Array { + let decoded; + try { + decoded = decodeURIComponent(value); + } catch (e) { + throw "Malformed data URL."; + } + return utf8Bytes(decoded); +} diff --git a/src/utils/types.ts b/src/utils/types.ts new file mode 100644 index 0000000..bf1b16e --- /dev/null +++ b/src/utils/types.ts @@ -0,0 +1,114 @@ +///////////////////// +///// Storage +///////////////////// + +export type StorageUploadParams = { + file: File; + id?: string; + name?: string; + bucketId?: string; +}; + +export type StorageUploadResponse = + | { fileMetadata: FileResponse; error: null } + | { fileMetadata: null; error: Error }; + +export type StorageGetUrlParams = { + fileId: string; +}; + +export type StorageGetPresignedUrlParams = { + fileId: string; +}; + +export type StorageGetPresignedUrlResponse = + | { presignedUrl: { url: string; expiration: number }; error: null } + | { presignedUrl: null; error: Error }; + +export type StorageDeleteParams = { + fileId: string; +}; + +export type StorageDeleteResponse = { error: Error | null }; + +///////////////////// +///// API +///////////////////// + +type FileResponse = { + id: string; + name: string; + size: number; + mimeType: string; + etag: string; + createdAt: string; + bucketId: string; +}; + +export type ApiUploadParams = { + file: FormData; + id?: string; + name?: string; + bucketId?: string; +}; + +export type ApiUploadResponse = + | { fileMetadata: FileResponse; error: null } + | { fileMetadata: null; error: Error }; + +export type ApiGetPresignedUrlParams = { + fileId: string; +}; + +export type ApiGetPresignedUrlResponse = + | { presignedUrl: { url: string; expiration: number }; error: null } + | { presignedUrl: null; error: Error }; + +export type ApiDeleteParams = { + fileId: string; +}; + +export type MetaObject = { + token?: string; +} + +export type HBPS3MetaData = { + key: string; + AcceptRanges?: string; + LastModified?: string; + ContentLength?: number; + ETag?: string; + ContentType?: string; + Metadata?: MetaObject +} + +export type StorageMetadataResponse = { + metadata: HBPS3MetaData | null, + error: Error | null +} + +export type ApiDeleteResponse = { error: Error | null }; + +export type StorageUploadFile = { + path: string; + file: File; + onUploadProgress: any | undefined; +}; + +export type StorageUploadString = { + path: string; + data: string; + type: "raw" | "data_url"; + metadata: { "content-type": string } | null; + onUploadProgress: any | undefined; +}; + +///////////////////// +///// MISC +///////////////////// + +export type UploadHeaders = { + "x-nhost-bucket-id"?: string; + "x-nhost-file-id"?: string; + "x-nhost-file-name"?: string; +}; diff --git a/test/index.spec.ts b/test/index.spec.ts index d84ce50..e69de29 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -1,8 +0,0 @@ -import { helloWorld } from './../src' -import { expect } from 'chai' - -describe('helloWorld', () => { - it('Should return greetings', () => { - expect(helloWorld()).equals("Hello from pakage!"); - }) -}) \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index a5162ff..cf12aef 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,19 +1,17 @@ { - "compilerOptions": { - "lib": ["es2017"], - "module": "commonjs", - "noImplicitReturns": true, - "esModuleInterop": true, - "outDir": "dist", - "sourceMap": true, - "target": "es5", - "declaration": true, - "types": [ - "mocha", "node" - ] - }, - "compileOnSave": true, - "include": [ - "src" - ] - } \ No newline at end of file + "include": ["src"], + "exclude": ["node_modules", "dist", "src/**/*.test.ts", "test"], + "compilerOptions": { + "lib": ["es2020", "dom"], + "esModuleInterop": true, + "declaration": true, + "strict": true, + "target": "es5", + "module": "esnext", + "outDir": "dist", + "rootDir": "src", + "moduleResolution": "node", + "sourceMap": true, + "noUnusedLocals": true + } +} \ No newline at end of file