diff --git a/packages/connect/Makefile b/packages/connect/Makefile index 57134c42..b550e1ab 100644 --- a/packages/connect/Makefile +++ b/packages/connect/Makefile @@ -2,14 +2,14 @@ clean: @rm -rf dist to-node test: - @deno lint && deno fmt && deno test -A --unstable deno/tests/*.ts + @deno lint && deno fmt && deno test -A --no-lock --unstable deno/tests/*.ts test-integration: clean to-node @cd ./node && yarn && yarn test to-node: @deno run --no-check --allow-read --allow-write \ - https://deno.land/x/deno2node@v1.3.0/src/cli.ts ./tsconfig.to-node.jsonc && \ + https://deno.land/x/deno2node@v1.6.0/src/cli.ts ./tsconfig.to-node.jsonc && \ yarn build && \ echo "copying types..." && find ./to-node -mindepth 1 -depth -name '*.d.ts' -exec cp --parents \{\} ./dist \; && \ mv ./dist/to-node/* ./dist && rm -rf ./dist/to-node diff --git a/packages/connect/deno/dev_deps.ts b/packages/connect/deno/dev_deps.ts index 46ffacff..f5e13b89 100644 --- a/packages/connect/deno/dev_deps.ts +++ b/packages/connect/deno/dev_deps.ts @@ -1,4 +1,4 @@ export { assert, assertEquals, -} from "https://deno.land/std@0.150.0/testing/asserts.ts"; +} from "https://deno.land/std@0.167.0/testing/asserts.ts"; diff --git a/packages/connect/deno/mod.ts b/packages/connect/deno/mod.ts index 92c6e47f..8e3120e1 100644 --- a/packages/connect/deno/mod.ts +++ b/packages/connect/deno/mod.ts @@ -33,11 +33,7 @@ export * from "./types.ts"; export { createHyperVerify } from "./utils/hyper-verify.ts"; -export function connect( - CONNECTION_STRING: string, - // deno-lint-ignore no-inferrable-types - domain: string = "default", -): Hyper { +export function connect(CONNECTION_STRING: string, domain = "default"): Hyper { const config = new URL(CONNECTION_STRING); const h = async (hyperRequest: HyperRequest) => { @@ -64,8 +60,6 @@ export function connect( .then((r) => (response.ok ? r : assoc("status", response.status, r))) .then((r) => (response.status >= 500 ? Promise.reject(r) : r)); - //const log = (x: any) => (console.log(x), x); - return { data: { add: (body) => diff --git a/packages/connect/deno/services/cache.ts b/packages/connect/deno/services/cache.ts index 706e34fa..b7db720a 100644 --- a/packages/connect/deno/services/cache.ts +++ b/packages/connect/deno/services/cache.ts @@ -4,6 +4,7 @@ import { HyperRequestFunction, Method, } from "../types.ts"; +import { HYPER_LEGACY_GET_HEADER } from "../utils/hyper-request.ts"; const service = "cache" as const; @@ -14,8 +15,16 @@ export const add = (key: string, value: unknown, ttl?: string) => (h: HyperRequestFunction) => h({ service, method: Method.POST, body: { key, value, ttl } }); -export const get = (key: string) => (h: HyperRequestFunction) => - h({ service, method: Method.GET, resource: key }); +export const get = (key: string) => (h: HyperRequestFunction) => { + return h({ + service, + method: Method.GET, + headers: new Headers({ + [HYPER_LEGACY_GET_HEADER]: "true", + }), + resource: key, + }); +}; export const remove = (key: string) => (h: HyperRequestFunction) => h({ service, method: Method.DELETE, resource: key }); @@ -23,12 +32,12 @@ export const remove = (key: string) => (h: HyperRequestFunction) => export const set = (key: string, value: unknown, ttl?: string) => (h: HyperRequestFunction) => h( - [{ service, method: Method.PUT, resource: key, body: value }] - .map(includeTTL(ttl))[0], + [{ service, method: Method.PUT, resource: key, body: value }].map( + includeTTL(ttl), + )[0], ); -// deno-lint-ignore no-inferrable-types -export const query = (pattern: string = "*") => (h: HyperRequestFunction) => +export const query = (pattern = "*") => (h: HyperRequestFunction) => h({ service, method: Method.POST, diff --git a/packages/connect/deno/services/data.ts b/packages/connect/deno/services/data.ts index 636d51ef..dbe7e7c6 100644 --- a/packages/connect/deno/services/data.ts +++ b/packages/connect/deno/services/data.ts @@ -7,13 +7,22 @@ import { Method, QueryOptions, } from "../types.ts"; +import { HYPER_LEGACY_GET_HEADER } from "../utils/hyper-request.ts"; const service = "data" as const; export const add = (body: unknown) => (hyper: HyperRequestFunction) => hyper({ service, method: Method.POST, body }); -export const get = (id: string) => (hyper: HyperRequestFunction) => - hyper({ service, method: Method.GET, resource: id }); +export const get = (id: string) => (hyper: HyperRequestFunction) => { + return hyper({ + service, + method: Method.GET, + headers: new Headers({ + [HYPER_LEGACY_GET_HEADER]: "true", + }), + resource: id, + }); +}; export const list = (options: ListOptions = {}) => (hyper: HyperRequestFunction) => hyper({ service, method: Method.GET, params: options }); diff --git a/packages/connect/deno/tests/cache.ts b/packages/connect/deno/tests/cache.ts index 09801a0a..db6a3f35 100644 --- a/packages/connect/deno/tests/cache.ts +++ b/packages/connect/deno/tests/cache.ts @@ -10,6 +10,7 @@ import { remove, set, } from "../services/cache.ts"; +import { HYPER_LEGACY_GET_HEADER } from "../utils/hyper-request.ts"; const test = Deno.test; @@ -37,8 +38,10 @@ test("cache.get", async () => { assertEquals(h.service, "cache"); assertEquals(h.method, "GET"); assertEquals(h.resource, "game-1"); + assertEquals(h.headers?.get(HYPER_LEGACY_GET_HEADER), "true"); return Promise.resolve(new Request("http://localhost")); }; + await get("game-1")(mockRequest); }); diff --git a/packages/connect/deno/tests/data.ts b/packages/connect/deno/tests/data.ts index f2dd4cd6..ec160071 100644 --- a/packages/connect/deno/tests/data.ts +++ b/packages/connect/deno/tests/data.ts @@ -13,6 +13,7 @@ import { remove, update, } from "../services/data.ts"; +import { HYPER_LEGACY_GET_HEADER } from "../utils/hyper-request.ts"; const test = Deno.test; @@ -38,8 +39,10 @@ test("data.get", async () => { assertEquals(h.service, "data"); assertEquals(h.method, "GET"); assertEquals(h.resource, "game-1"); + assertEquals(h.headers?.get(HYPER_LEGACY_GET_HEADER), "true"); return Promise.resolve(new Request("http://localhost")); }; + await get("game-1")(mockRequest); }); diff --git a/packages/connect/deno/tests/hyper-request.ts b/packages/connect/deno/tests/hyper-request.ts index dd07d728..a4429b30 100644 --- a/packages/connect/deno/tests/hyper-request.ts +++ b/packages/connect/deno/tests/hyper-request.ts @@ -1,14 +1,140 @@ import { assert, assertEquals } from "../dev_deps.ts"; import { generateToken } from "../deps.deno.ts"; +import { hyper, HYPER_LEGACY_GET_HEADER } from "../utils/hyper-request.ts"; +import { HyperRequest } from "../types.ts"; -Deno.test("generateToken", async () => { - try { - const res = await generateToken("SUB", "SECRET"); - assert(true); - assertEquals(typeof res, "string"); - // deno-lint-ignore no-explicit-any - } catch (error: any) { - assert(false, error.message); - } +Deno.test("hyper-request", async (t) => { + await t.step("generateToken", async (t) => { + await t.step("should generate a token successfully", async () => { + try { + const res = await generateToken("SUB", "SECRET"); + assert(true); + assertEquals(typeof res, "string"); + // deno-lint-ignore no-explicit-any + } catch (error: any) { + assert(false, error.message); + } + }); + }); + + await t.step("hyper", async (t) => { + const req: HyperRequest = { + service: "data", + method: "GET", + }; + + await t.step("url", async (t) => { + await t.step("it should append params", async () => { + const resource = await hyper( + new URL("cloud://mock.hyper.io/foobar"), + "default", + )({ + ...req, + params: { + foo: "bar", + }, + }); + assertEquals( + resource.url, + "https://mock.hyper.io/foobar/data/default?foo=bar", + ); + }); + + await t.step("isCloud", async (t) => { + const cloudCs = "cloud://mock.hyper.io/foobar"; + + await t.step("should build the resource url correctly", async () => { + const resource = await hyper(new URL(cloudCs), "default")(req); + assertEquals( + resource.url, + "https://mock.hyper.io/foobar/data/default", + ); + }); + + await t.step("should build the action url correctly", async () => { + const action = await hyper( + new URL(cloudCs), + "default", + )({ ...req, action: "_bulk" }); + assertEquals( + action.url, + "https://mock.hyper.io/foobar/data/default/_bulk", + ); + }); + }); + + await t.step("not isCloud", async (t) => { + const notCloud = "http://localhost:6363/foobar"; + + await t.step("should build the resource url correctly", async () => { + const resource = await hyper(new URL(notCloud), "default")(req); + assertEquals(resource.url, "http://localhost:6363/data/foobar"); + }); + + await t.step("should build the action url correctly", async () => { + const action = await hyper( + new URL(notCloud), + "default", + )({ ...req, action: "_bulk" }); + assertEquals(action.url, "http://localhost:6363/data/foobar/_bulk"); + }); + }); + }); + + await t.step("options", async (t) => { + await t.step("headers", async (t) => { + await t.step("should set the Authorization header", async () => { + const resource = await hyper( + new URL("http://foo:bar@localhost:6363/foobar"), + "default", + )(req); + assert(resource.options?.headers.has("Authorization")); + }); + + await t.step("should set the Content-Type header", async () => { + const resource = await hyper( + new URL("http://foo:bar@localhost:6363/foobar"), + "default", + )(req); + assert(resource.options?.headers.has("Content-Type")); + }); + + await t.step("should set any provided headers", async () => { + const resource = await hyper( + new URL("http://foo:bar@localhost:6363/foobar"), + "default", + )({ + ...req, + headers: new Headers({ [HYPER_LEGACY_GET_HEADER]: "true" }), + }); + assert(resource.options?.headers.has(HYPER_LEGACY_GET_HEADER)); + }); + }); + + await t.step("should add the body", async () => { + const resource = await hyper( + new URL("http://localhost:6363/foobar"), + "default", + )({ ...req, body: { foo: "bar" } }); + assertEquals(resource.options?.body, JSON.stringify({ foo: "bar" })); + }); + + await t.step("should add the method", async () => { + const resource = await hyper( + new URL("http://localhost:6363/foobar"), + "default", + )(req); + assertEquals(resource.options?.method, "GET"); + + const resource2 = await hyper( + new URL("http://localhost:6363/foobar"), + "default", + // deno-lint-ignore ban-ts-comment + // @ts-ignore + )({ ...req, method: undefined }); + assertEquals(resource2.options?.method, "GET"); + }); + }); + }); }); diff --git a/packages/connect/deno/types.ts b/packages/connect/deno/types.ts index 7c048c48..a72d57e6 100644 --- a/packages/connect/deno/types.ts +++ b/packages/connect/deno/types.ts @@ -169,6 +169,7 @@ export interface HyperRequest { service: "data" | "cache" | "storage" | "search" | "queue" | "info"; method: Method; resource?: string; + headers?: Headers; body?: unknown; // deno-lint-ignore no-explicit-any params?: undefined | Record; diff --git a/packages/connect/deno/utils/hyper-request.ts b/packages/connect/deno/utils/hyper-request.ts index bddf8f59..9593462d 100644 --- a/packages/connect/deno/utils/hyper-request.ts +++ b/packages/connect/deno/utils/hyper-request.ts @@ -14,15 +14,24 @@ interface HyperRequestParams { options?: RequestOptions; } +export const HYPER_LEGACY_GET_HEADER = "X-HYPER-LEGACY-GET"; + export const hyper = (conn: URL, domain: string) => -async ( - { service, method, resource, body, params, action }: HyperRequest, -): Promise => { +async ({ + service, + method, + headers, + resource, + body, + params, + action, +}: HyperRequest): Promise => { const isCloud = /^cloud/.test(conn.protocol); const protocol = isCloud ? "https:" : conn.protocol; let options = { headers: new Headers({ + ...(headers ? Object.fromEntries(headers.entries()) : {}), "Content-Type": "application/json", }), method: method ? method : Method.GET, diff --git a/packages/connect/package.json b/packages/connect/package.json index 3b38da51..b7824069 100644 --- a/packages/connect/package.json +++ b/packages/connect/package.json @@ -23,17 +23,17 @@ "build": "microbundle --tsconfig tsconfig.ci.jsonc" }, "dependencies": { - "@deno/shim-deno": "^0.9.0", + "@deno/shim-deno": "^0.11.0", "crocks": "^0.12.4", - "jose": "^4.9.1", + "jose": "^4.11.1", "ms": "^2.1.3", "ramda": "^0.28.0", - "undici": "^5.10.0" + "undici": "^5.14.0" }, "devDependencies": { "@types/ms": "^0.7.31", - "@types/node": "^16.11.56", - "@types/ramda": "^0.28.15", + "@types/node": "^18.11.14", + "@types/ramda": "^0.28.20", "microbundle": "^0.15.1" } } diff --git a/packages/connect/yarn.lock b/packages/connect/yarn.lock index 2ee28841..f0a8bc79 100644 --- a/packages/connect/yarn.lock +++ b/packages/connect/yarn.lock @@ -979,17 +979,17 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" -"@deno/shim-deno-test@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@deno/shim-deno-test/-/shim-deno-test-0.3.2.tgz#03f258363ba0f57310188e888f8f17629fcdc59d" - integrity sha512-STxWMnLuA0/bT0F41yKx6AbyzVVSjXYvgleFE4xit/m4aooMG/0Bs0MiQUe95EVS8DrzYmWbuCtdIlWymAdlsw== +"@deno/shim-deno-test@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@deno/shim-deno-test/-/shim-deno-test-0.4.0.tgz#2ff56821854c51323c0cd08a4a56d668f84367ba" + integrity sha512-oYWcD7CpERZy/TXMTM9Tgh1HD/POHlbY9WpzmAk+5H8DohcxG415Qws8yLGlim3EaKBT2v3lJv01x4G0BosnaQ== -"@deno/shim-deno@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@deno/shim-deno/-/shim-deno-0.9.0.tgz#b70d8daee67210c45f316006f4b65144e0a62519" - integrity sha512-iP+qdI4Oy/Mw9yv40TqdjNKL+stpKDo8drki2cKisTXgZf+GoIdMhIuODxSypRyv6wxIuHNx7ZiKE3Sl3kAHuw== +"@deno/shim-deno@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@deno/shim-deno/-/shim-deno-0.11.0.tgz#02b3e56feb8ecca8caf8498ad3de48f0d91e23e9" + integrity sha512-z5FRYleehfz1Z0Wp9jilB98cJRGV2HYiLN0FZu1/xwLrQk+hk5VaNKp1nEoyGrUM4MLruTlnhEbqHJwxfnbPLw== dependencies: - "@deno/shim-deno-test" "^0.3.2" + "@deno/shim-deno-test" "^0.4.0" which "^2.0.2" "@jridgewell/gen-mapping@^0.1.0": @@ -1122,20 +1122,20 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.32.tgz#51d59d7a90ef2d0ae961791e0900cad2393a0149" integrity sha512-eAIcfAvhf/BkHcf4pkLJ7ECpBAhh9kcxRBpip9cTiO+hf+aJrsxYxBeS6OXvOd9WqNAJmavXVpZvY1rBjNsXmw== -"@types/node@^16.11.56": - version "16.11.56" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.56.tgz#dcbb617669481e158e0f1c6204d1c768cd675901" - integrity sha512-aFcUkv7EddxxOa/9f74DINReQ/celqH8DiB3fRYgVDM2Xm5QJL8sl80QKuAnGvwAsMn+H3IFA6WCrQh1CY7m1A== +"@types/node@^18.11.14": + version "18.11.14" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.14.tgz#a8571b25f3a31e9ded14e3ab9488509adef831d8" + integrity sha512-0KXV57tENYmmJMl+FekeW9V3O/rlcqGQQJ/hNh9r8pKIj304pskWuEd8fCyNT86g/TpO0gcOTiLzsHLEURFMIQ== "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/ramda@^0.28.15": - version "0.28.15" - resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.28.15.tgz#36bb4c8de430e3bbcd29590537569aef64409716" - integrity sha512-FCaLNVZry65jW8x/FDnKgjgkCNQxgc5AYMQwdNn6yW5M+62R+0nt2Y36U43dTNora9hcquemfrY5gxhE5pcilQ== +"@types/ramda@^0.28.20": + version "0.28.20" + resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.28.20.tgz#df93bb5674f0051464c075480ca3075d1c1361ba" + integrity sha512-MeUhzGSXQTRsY19JGn5LIBTLxVEnyF6HDNr08KSJqybsm4DlfLIgK1jBHjhpiSyk252tXYmp+UOe0UFg0UiFsA== dependencies: ts-toolbelt "^6.15.1" @@ -1307,6 +1307,13 @@ builtin-modules@^3.1.0: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== +busboy@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -2136,10 +2143,10 @@ jest-worker@^26.2.1: merge-stream "^2.0.0" supports-color "^7.0.0" -jose@^4.9.1: - version "4.9.1" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.9.1.tgz#c1eef9f20f479d8aa55cdfd4bbc3d76322dd1b48" - integrity sha512-ETgCfJ2yxJavpJdVMgznN8ot3MJyZUiLyY2xiZ2sSNL/uEZh1EH74cmNYWhZgMSBwSdSz03ja5cMZU/9BSlqXg== +jose@^4.11.1: + version "4.11.1" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.11.1.tgz#8f7443549befe5bddcf4bae664a9cbc1a62da4fa" + integrity sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q== js-tokens@^4.0.0: version "4.0.0" @@ -3071,6 +3078,11 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + string-hash@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" @@ -3236,10 +3248,12 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -undici@^5.10.0: - version "5.10.0" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.10.0.tgz#dd9391087a90ccfbd007568db458674232ebf014" - integrity sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g== +undici@^5.14.0: + version "5.14.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.14.0.tgz#1169d0cdee06a4ffdd30810f6228d57998884d00" + integrity sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ== + dependencies: + busboy "^1.6.0" unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0"