Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript - isomorphic setup, first set of middlewares #728

Merged
merged 46 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
fd28d27
adding client factory, context and middleware files
nikithauc Sep 1, 2021
1e1a966
Adding middlewarefactory
nikithauc Sep 2, 2021
476532d
introduce custom fetch
nikithauc Sep 14, 2021
5ddd92e
Merge branch 'main' into enhancement/nikithauc-client-middleware-setup
nikithauc Sep 20, 2021
0f4d006
removed middlewareoption getkey, httpclient factory,
nikithauc Sep 22, 2021
454f0a6
adding MiddlewareControl
nikithauc Sep 26, 2021
9b09cdc
Merge branch 'main' into enhancement/nikithauc-client-middleware-setup
nikithauc Oct 3, 2021
1528921
adding shims
nikithauc Oct 4, 2021
c3a31b5
adding fetch definitions
nikithauc Oct 6, 2021
02d5812
Adding dom.d.ts, url utils
nikithauc Oct 7, 2021
9b9c1b0
splitting tsconfig
nikithauc Oct 8, 2021
090c201
adding redirect and middlewareutil tests
nikithauc Oct 9, 2021
64640dc
add karma tests
nikithauc Oct 12, 2021
de1dfdc
installing rollup, http client tests node
nikithauc Oct 12, 2021
cf02bd8
setting the browser tests
nikithauc Oct 14, 2021
2929fb0
Merge branch 'main' into enhancement/nikithauc-client-middleware-setup
nikithauc Oct 14, 2021
5aaca76
adding abstraction tests
nikithauc Oct 15, 2021
a43fbe8
moving shims, import from node-fetch
nikithauc Oct 15, 2021
8c2f25e
adding eslint
nikithauc Oct 15, 2021
21be144
adding eslint to abstractions
nikithauc Oct 16, 2021
a3089a7
Merge branch 'main' into enhancement/nikithauc-client-middleware-setup
nikithauc Oct 16, 2021
ed1580f
middleware return type, getkey definition
nikithauc Oct 26, 2021
e6dcdce
updating tests
nikithauc Oct 28, 2021
b65651e
Merge branch 'main' into enhancement/nikithauc-client-middleware-setup
nikithauc Oct 28, 2021
f634353
adding url util
nikithauc Oct 28, 2021
4d2728c
Merge branch 'main' into enhancement/nikithauc-client-middleware-setup
nikithauc Nov 8, 2021
af9a3c8
removing url utils
nikithauc Nov 9, 2021
7decd35
minor updates
nikithauc Nov 9, 2021
472f924
Merge branch 'main' into enhancement/nikithauc-client-middleware-setup
nikithauc Feb 2, 2022
0274947
deleting the context
nikithauc Feb 2, 2022
2725c92
Merge branch 'main' into enhancement/nikithauc-client-middleware-setup
nikithauc Feb 4, 2022
d5acde3
Merge branch 'main' into enhancement/nikithauc-client-middleware-setup
nikithauc Feb 5, 2022
889c87a
Merge branch 'main' into enhancement/nikithauc-client-middleware-setup
nikithauc Mar 15, 2022
142b70f
removing url util abstractions
nikithauc Mar 15, 2022
e0992a8
using fetch definitions only when required
nikithauc Mar 15, 2022
970f6f6
splitting test src tsconfigs, adding null checks
nikithauc Mar 17, 2022
f4fc008
update tests
nikithauc Mar 18, 2022
3db1f8c
resetting abstractions
nikithauc Mar 18, 2022
e34194d
Apply suggestions from code review
nikithauc Mar 18, 2022
3f5b400
resetting abstractions
nikithauc Mar 18, 2022
c7c654b
Merge branch 'main' into enhancement/nikithauc-client-middleware-setup
nikithauc Mar 18, 2022
aead7b0
update version, use set, collapse function
nikithauc Mar 21, 2022
c3667ae
Merge branch 'main' into enhancement/nikithauc-client-middleware-setup
nikithauc Mar 21, 2022
a6fb496
update change logi=
nikithauc Mar 21, 2022
973a69c
Correct case
nikithauc Mar 21, 2022
be93925
version bump, case sensitive check
nikithauc Mar 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

### Changed
- Moving middlewares from Graph core

## [0.0.19] - 2022-03-18

Expand Down
3 changes: 1 addition & 2 deletions http/typescript/fetch/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@
*.d.ts

node_modules
lib
spec/development
dist
4 changes: 3 additions & 1 deletion http/typescript/fetch/.npmignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
tsconfig.json
tsconfig.json
dist/cjs/test
dist/es/test
148 changes: 148 additions & 0 deletions http/typescript/fetch/docs/design/isomorphic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
## Terms -

- bundler - Module bundlers are tools frontend developers used to bundle JavaScript modules into a single JavaScript files that can be executed in the browser.

- rollup - rollup.js is the module bundler that the JS SDK uses.

- package.json fields -

- main - The main field is a module ID that is the primary entry point to your program. Points to the CJS modules.

- module - The module field is not an official npm feature but a common convention among bundlers to designate how to import an ESM version of a library. Points to the ES modules.

- browser - If the module is meant to be used client-side, the browser field should be used instead of the main field.

## Isomorphic library
The Kiota libraries are isomorphic, that is, this library runs in node and browser.

To achieve the isomorphism, the library is set up in the following ways:

TypeScript Source Code
/ \
Transpiles into JavaScript
'dist' folder
/ \
CJS module ES modules
1. The library supports `CommonJS` modules and `ES` modules.`CommonJS` formats are popular in the node enviroment.
`main` - `dist/es/src/index.js`
`module` - `dist/es/src/index.js`

2. Entry point for the browser - `dist/es/src/browser/index.js`.

Often times the library will have code which is different for the browser and node environments.
Examples:
- Browsers use a global `fetch` defined in the `DOM` while node relies on an external library such as `node-fetch` or `undici`.
- `ReadableStream` are different interfaces in node and browser.

To manage such differences, separate files are maintained when the code can be different for node or browser.

For example:
- The `DefaultFetchHandler` uses `node-fetch` for node and the `dom` fetch for browser.
- Map this difference as follows:

```json
"browser": {
"./dist/es/src/index.js": "./dist/es/src/browser/index.js",
"./dist/es/src/utils/utils.js": "./dist/es/src/utils/browser/utils.js",
"./dist/es/src/middlewares/defaultFetchHandler.js": "./dist/es/src/middlewares/browser/defaultFetchHandler.js",
"./dist/es/src/middlewares/middlewareFactory.js": "./dist/es/src/middlewares/browser/middlewareFactory.js",
"./dist/cjs/src/middlewares/middlewareFactory.js": "./dist/es/src/middlewares/browser/middlewareFactory.js"
}
```

The applications or library using `kiota-http-fetch library` will have bundlers that use this `package.json browser` mapping if the target environment is set to browser.


## Use of `lib:dom` and `@types/node`

Typescript isomorphic libraries often rely on the DOM definitions and `@types/node` as compile-time dependencies.

Example 1:
```json
// tsconfig.json in the isomorphic library

{
"compileroptions": {
"lib":"dom"
}
}
```
Issues with example 1:
- This configuration makes DOM definitions globally available as a compile time dependency during development and it is not shipped with the library. However, when a node only application uses the isomorphic library, the DOM definitions will not be available and this causes Typescript compile time errors.
- [Issue example: Removed dom lib dependency](https://github.com/prisma-labs/graphql-request/issues/26)
- [PR example: azure core-http](https://github.com/Azure/azure-sdk-for-js/pull/7500)


Example 2:
```
// example of use of @type/nodes
const readableStream : NodeJS.ReadableStream

or

global ambient declarations at the top of the file
/// <reference lib="dom" />
/// <reference lib="node" />
```

Issues with example 2:
- When transpiling a direct reference to `@types/node`, such as `NodeJS.ReadableStream`, the transpiled code contains a global ambient declaration `/// <reference lib="node" />`. The global ambient declarations leak the typings to the user code.
- Example of issues:
- [Removes reference to node typings and stubs dom interfaces](https://github.com/aws/aws-sdk-js/pull/1228)
- [Typings polutes global space with DOM types ](https://github.com/node-fetch/node-fetch/issues/1285)


Other issues:
Typescript is yet capture some of complete dynamic support that JavaScript provides. An example of this is [Node browser resolution strategy](https://github.com/microsoft/TypeScript/issues/7753) since today the compiler cannot type check based on the environment.

### Solution to this:

Approaches for this problem as observed in other isomorphic libraries:
- A good reference to a very optimal solution is here: [PR example: azure core-http](https://github.com/Azure/azure-sdk-for-js/pull/7500)
- A common approch that is taken is to create empty interfaces or shims. Then, if the user includes dom or the required definitions, the interfaces are merged. Reference [Removes reference to node typings and stubs dom interfaces](https://github.com/aws/aws-sdk-js/pull/1228)

##### DOM shims and Fetch definitions
The `kiota-http-fetch library` relies on the DOM definitions such as `RequestInit`, `RequestInfo`.

1. Maintain and export `dom.shim.d.ts` containing empty interfaces. This way the user can compile their code with the library without having a DOM library.

```typescript
interface Request { }
interface RequestInit { }
interface Response { }
interface Headers { }
interface ReadableStream { }
interface fetch { }
```

2. The library code relies on interface definitions and interface properties such as `Request.body`, `Request.headers`. Introduced the `FetchDefinitions.ts` to achieve this. `FetchDefinitions.ts` contains the following intersection types:
- `FetchRequestInit`
- `FetchHeadersInit`
- `FetchResponse`

These types are redefined so that definitions are available and can be set isomorphically.

For example:

``` typescript
export type FetchRequestInit = Omit<RequestInit, "body" | "headers" | "redirect" | "signal"> & {
/**
* Request's body
* Expected type in case of dom - ReadableStream | XMLHttpRequestBodyInit|null
* Expected type in case of node-fetch - | Blob | Buffer | URLSearchParams | NodeJS.ReadableStream | string|null
*/
body?: unknown;
}
````

##### ReadableStreamContent

- `ReadableStream` are different interfaces in node and browser.
- Introducing `ReadableStreamContent`, empty interface expecting the type of NodeJS.ReadableStream or dom ReadableStream
* Node example: import {Readable} from "stream"; const readableStream = new Readable();
* Browser example: const readableStream = new ReadableStream();

Alternatives considered to manage this difference:
- `content - NodeJS.ReadableStream | ReadableStream`. This transpiles to use the global ambient declaration of `type = nodes` which causes the global leaking issues discussed above.
- `content - ReadableStream` and maintaining an empty interface with the same name, that is, ReadableStream causes a mismatch error if content is set to NodeJS.Readable and DOM definitions are present at the same time.
- Due to the lack of `node browser resolution strategy` in the typings, we cannot maintain separate node and browser typings files for `ReadableStream` interface.
25 changes: 25 additions & 0 deletions http/typescript/fetch/docs/design/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Testing for node and browser

### Testing for node

- Tests targeting the node environment are in `/test/node` and `/test/common` folder and use `mocha` and `chai` JS libraries.
- Test formats:
- script to test `CommonJS` modules: `npm run test:cjs`
- script to test `ES` modules: `npm run test:es`
- Examples of node environment specific tests: Test `DefaultFetchHandler` using `node-fetch` library.


### Testing for browser


- Tests targeting the node environment are in `/test/browser` and `/test/common` folder and use `mocha` and `chai` JS libraries.
- To test for browsers, the tests and the source code are bundled using `rollup` and the bundled file is tested using `karma`.
- Test formats:
- script to test: `npm run karma`.
- Examples of node environment specific tests: Test `DefaultFetchHandler` using dom - `fetch`.

---
**NOTE**

The bundled file considers the `package.json browser spec` during the rollup process. The entry point of the source code for the tests will be `src/browser/index.js` and the `package.json browser spec` file mapping should work.
---
22 changes: 22 additions & 0 deletions http/typescript/fetch/dom.shim.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/


/**
* Maintain and export `dom.shim.d.ts` containing empty interfaces. This way the user can compile their code with the library without having a DOM library
* Read more {@link docs/design/isomorphic.md}
* This file should be shipped in the package
* */


interface Request {}
interface RequestInit {}
interface Response {}
interface Headers {}
interface ReadableStream {}
interface fetch {}
interface Blob {}
13 changes: 13 additions & 0 deletions http/typescript/fetch/karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module.exports = function(config) {
config.set({
frameworks: ["mocha", "chai", "karma-typescript"],
files: [{ pattern: "dist/es/test/index.js", type: "module" }],
preprocessors: {
"**/*.ts": ["karma-typescript"],
},
karmaTypescriptConfig: {
tsconfig: "./tsconfig.cjs.json",
},
browsers: ["ChromeHeadless"],
});
};
Loading