Update supported Node.js versions and dependencies, implement TypeScript JSDoc types.

This commit is contained in:
Jayden Seric 2022-05-24 00:06:08 +10:00
parent a56972742c
commit 86e5609e60
25 changed files with 626 additions and 249 deletions

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"typescript.disableAutomaticTypeAcquisition": true,
"typescript.enablePromptUseWorkspaceTsdk": true,
"typescript.tsdk": "api/node_modules/typescript/lib"
}

View File

@ -1 +1,3 @@
// @ts-check
export default new URL("../uploads/", import.meta.url);

11
api/jsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"maxNodeModuleJsDepth": 10,
"module": "nodenext",
"noEmit": true,
"strict": true
},
"typeAcquisition": {
"enable": false
}
}

325
api/package-lock.json generated
View File

@ -10,19 +10,22 @@
"apollo-server-koa": "^3.7.0",
"dotenv": "^16.0.1",
"graphql": "^16.5.0",
"graphql-upload": "^13.0.0",
"graphql-upload": "^14.0.0",
"koa": "^2.13.4",
"make-dir": "^3.1.0",
"shortid": "^2.2.16"
},
"devDependencies": {
"@types/koa": "^2.13.4",
"@types/node": "^17.0.35",
"eslint": "^8.16.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"nodemon": "^2.0.16",
"prettier": "^2.6.2"
"prettier": "^2.6.2",
"typescript": "^4.7.1-rc"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >= 16.0.0",
"node": "^14.17.0 || ^16.0.0 || >= 18.0.0",
"npm": ">= 7"
},
"funding": {
@ -283,6 +286,14 @@
"@types/node": "*"
}
},
"node_modules/@types/busboy": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-0.3.2.tgz",
"integrity": "sha512-iEvdm9Z9KdSs/ozuh1Z7ZsXrOl8F4M/CLMXPZHr3QuJ4d6Bjn+HBMC5EMKpwpAo8oi8iK9GZfFoHaIMrrZgwVw==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/connect": {
"version": "3.4.35",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
@ -397,6 +408,11 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.35.tgz",
"integrity": "sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg=="
},
"node_modules/@types/object-path": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/@types/object-path/-/object-path-0.11.1.tgz",
"integrity": "sha512-219LSCO9HPcoXcRTC6DbCs0FRhZgBnEMzf16RRqkT40WbkKx3mOeQuz3e2XqbfhOz/AHfbru0kzB1n1RCAsIIg=="
},
"node_modules/@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@ -1018,14 +1034,6 @@
"node": ">= 0.8"
}
},
"node_modules/cookies/node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/copy-to": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/copy-to/-/copy-to-2.0.1.tgz",
@ -1119,11 +1127,11 @@
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"node_modules/depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"engines": {
"node": ">= 0.6"
"node": ">= 0.8"
}
},
"node_modules/destroy": {
@ -1649,23 +1657,36 @@
}
},
"node_modules/graphql-upload": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-13.0.0.tgz",
"integrity": "sha512-YKhx8m/uOtKu4Y1UzBFJhbBGJTlk7k4CydlUUiNrtxnwZv0WigbRHP+DVhRNKt7u7DXOtcKZeYJlGtnMXvreXA==",
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-14.0.0.tgz",
"integrity": "sha512-l3utaN0p/VLUFFrUAijwEfmsSxEolVri5uuD91yhbOkxRD6q5l0rsy1F4Naq4TKWFSKsGciCjyrc1ZhCT2Ew7A==",
"dependencies": {
"@types/busboy": "^0.3.2",
"@types/node": "*",
"@types/object-path": "^0.11.1",
"busboy": "^0.3.1",
"fs-capacitor": "^6.2.0",
"http-errors": "^1.8.1",
"http-errors": "^2.0.0",
"object-path": "^0.11.8"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >= 16.0.0"
"node": "^14.17.0 || ^16.0.0 || >= 18.0.0"
},
"funding": {
"url": "https://github.com/sponsors/jaydenseric"
},
"peerDependencies": {
"graphql": "0.13.1 - 16"
"@types/express": "^4.0.29",
"@types/koa": "^2.11.4",
"graphql": "^16.3.0"
},
"peerDependenciesMeta": {
"@types/express": {
"optional": true
},
"@types/koa": {
"optional": true
}
}
},
"node_modules/has": {
@ -1734,13 +1755,15 @@
"node": ">= 0.8"
}
},
"node_modules/http-cache-semantics": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
"dev": true
"node_modules/http-assert/node_modules/depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/http-errors": {
"node_modules/http-assert/node_modules/http-errors": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
"integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
@ -1755,6 +1778,35 @@
"node": ">= 0.6"
}
},
"node_modules/http-assert/node_modules/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/http-cache-semantics": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
"dev": true
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -2100,12 +2152,35 @@
"node": ">= 10"
}
},
"node_modules/koa/node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"node_modules/koa/node_modules/http-errors": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
"integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
"dependencies": {
"depd": "~1.1.2",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
"node": ">= 0.6"
}
},
"node_modules/koa/node_modules/http-errors/node_modules/depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/koa/node_modules/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/latest-version": {
@ -2623,37 +2698,6 @@
"node": ">= 0.8"
}
},
"node_modules/raw-body/node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/raw-body/node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/raw-body/node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@ -2883,11 +2927,11 @@
"dev": true
},
"node_modules/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"engines": {
"node": ">= 0.6"
"node": ">= 0.8"
}
},
"node_modules/streamsearch": {
@ -3058,6 +3102,19 @@
"is-typedarray": "^1.0.0"
}
},
"node_modules/typescript": {
"version": "4.7.1-rc",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.1-rc.tgz",
"integrity": "sha512-EQd2NVelDe6ZVc2sO1CSpuSs+RHzY8c2n/kTNQAHw4um/eAXY+ZY4IKoUpNK0wO6C5hN+XcUXR7yqT8VbwwNIQ==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
@ -3528,6 +3585,14 @@
"@types/node": "*"
}
},
"@types/busboy": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-0.3.2.tgz",
"integrity": "sha512-iEvdm9Z9KdSs/ozuh1Z7ZsXrOl8F4M/CLMXPZHr3QuJ4d6Bjn+HBMC5EMKpwpAo8oi8iK9GZfFoHaIMrrZgwVw==",
"requires": {
"@types/node": "*"
}
},
"@types/connect": {
"version": "3.4.35",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
@ -3642,6 +3707,11 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.35.tgz",
"integrity": "sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg=="
},
"@types/object-path": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/@types/object-path/-/object-path-0.11.1.tgz",
"integrity": "sha512-219LSCO9HPcoXcRTC6DbCs0FRhZgBnEMzf16RRqkT40WbkKx3mOeQuz3e2XqbfhOz/AHfbru0kzB1n1RCAsIIg=="
},
"@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@ -4109,13 +4179,6 @@
"requires": {
"depd": "~2.0.0",
"keygrip": "~1.1.0"
},
"dependencies": {
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
}
}
},
"copy-to": {
@ -4191,9 +4254,9 @@
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
"destroy": {
"version": "1.2.0",
@ -4587,13 +4650,16 @@
}
},
"graphql-upload": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-13.0.0.tgz",
"integrity": "sha512-YKhx8m/uOtKu4Y1UzBFJhbBGJTlk7k4CydlUUiNrtxnwZv0WigbRHP+DVhRNKt7u7DXOtcKZeYJlGtnMXvreXA==",
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-14.0.0.tgz",
"integrity": "sha512-l3utaN0p/VLUFFrUAijwEfmsSxEolVri5uuD91yhbOkxRD6q5l0rsy1F4Naq4TKWFSKsGciCjyrc1ZhCT2Ew7A==",
"requires": {
"@types/busboy": "^0.3.2",
"@types/node": "*",
"@types/object-path": "^0.11.1",
"busboy": "^0.3.1",
"fs-capacitor": "^6.2.0",
"http-errors": "^1.8.1",
"http-errors": "^2.0.0",
"object-path": "^0.11.8"
}
},
@ -4637,13 +4703,12 @@
"requires": {
"deep-equal": "~1.0.1",
"http-errors": "~1.8.0"
}
},
"http-cache-semantics": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
"dev": true
"dependencies": {
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"http-errors": {
"version": "1.8.1",
@ -4657,6 +4722,31 @@
"toidentifier": "1.0.1"
}
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
}
}
},
"http-cache-semantics": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
"dev": true
},
"http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"requires": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -4897,11 +4987,30 @@
"type-is": "^1.6.16",
"vary": "^1.1.2"
},
"dependencies": {
"http-errors": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
"integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.1"
},
"dependencies": {
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
}
}
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
}
}
},
@ -5291,30 +5400,6 @@
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"dependencies": {
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
"http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"requires": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
}
},
"statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
}
}
},
"rc": {
@ -5483,9 +5568,9 @@
"dev": true
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
},
"streamsearch": {
"version": "0.1.2",
@ -5610,6 +5695,12 @@
"is-typedarray": "^1.0.0"
}
},
"typescript": {
"version": "4.7.1-rc",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.1-rc.tgz",
"integrity": "sha512-EQd2NVelDe6ZVc2sO1CSpuSs+RHzY8c2n/kTNQAHw4um/eAXY+ZY4IKoUpNK0wO6C5hN+XcUXR7yqT8VbwwNIQ==",
"dev": true
},
"undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",

View File

@ -16,29 +16,33 @@
"bugs": "https://github.com/jaydenseric/apollo-upload-examples/issues",
"funding": "https://github.com/sponsors/jaydenseric",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >= 16.0.0",
"node": "^14.17.0 || ^16.0.0 || >= 18.0.0",
"npm": ">= 7"
},
"dependencies": {
"apollo-server-koa": "^3.7.0",
"dotenv": "^16.0.1",
"graphql": "^16.5.0",
"graphql-upload": "^13.0.0",
"graphql-upload": "^14.0.0",
"koa": "^2.13.4",
"make-dir": "^3.1.0",
"shortid": "^2.2.16"
},
"devDependencies": {
"@types/koa": "^2.13.4",
"@types/node": "^17.0.35",
"eslint": "^8.16.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"nodemon": "^2.0.16",
"prettier": "^2.6.2"
"prettier": "^2.6.2",
"typescript": "^4.7.1-rc"
},
"scripts": {
"dev": "nodemon",
"start": "node -r dotenv/config server.mjs",
"eslint": "eslint .",
"prettier": "prettier -c .",
"test": "npm run eslint && npm run prettier"
"types": "tsc -p jsconfig.json",
"test": "npm run eslint && npm run prettier && npm run types"
}
}

View File

@ -1,3 +1,5 @@
// @ts-check
import { GraphQLNonNull, GraphQLObjectType, GraphQLString } from "graphql";
import UPLOAD_DIRECTORY_URL from "../config/UPLOAD_DIRECTORY_URL.mjs";

View File

@ -1,5 +1,7 @@
// @ts-check
import { GraphQLList, GraphQLNonNull, GraphQLObjectType } from "graphql";
import GraphQLUpload from "graphql-upload/public/GraphQLUpload.js";
import GraphQLUpload from "graphql-upload/GraphQLUpload.js";
import storeUpload from "../storeUpload.mjs";
import FileType from "./FileType.mjs";
@ -29,15 +31,25 @@ export default new GraphQLObjectType({
),
},
},
async resolve(parent, { files }) {
async resolve(
parent,
/**
* @type {{ files: Array<
* Promise<import("graphql-upload/processRequest.js").FileUpload>
* >}}
*/
{ files }
) {
/** @type {Array<string>} */
const storedFileNames = [];
// Ensure an error storing one upload doesnt prevent storing the rest.
const results = await Promise.allSettled(files.map(storeUpload));
return results.reduce((storedFiles, { value, reason }) => {
if (value) storedFiles.push(value);
for (const result of await Promise.allSettled(files.map(storeUpload)))
if ("value" in result) storedFileNames.push(result.value);
// Realistically you would do more than just log an error.
else console.error(`Failed to store upload: ${reason}`);
return storedFiles;
}, []);
else console.error(`Failed to store upload: ${result.reason}`);
return storedFileNames;
},
},
}),

View File

@ -1,3 +1,5 @@
// @ts-check
import fs from "fs";
import { GraphQLList, GraphQLNonNull, GraphQLObjectType } from "graphql";

View File

@ -1,3 +1,5 @@
// @ts-check
import { GraphQLSchema } from "graphql";
import MutationType from "./MutationType.mjs";

View File

@ -1,5 +1,7 @@
// @ts-check
import { ApolloServer } from "apollo-server-koa";
import graphqlUploadKoa from "graphql-upload/public/graphqlUploadKoa.js";
import graphqlUploadKoa from "graphql-upload/graphqlUploadKoa.js";
import Koa from "koa";
import makeDir from "make-dir";
import { fileURLToPath } from "url";
@ -7,9 +9,7 @@ import { fileURLToPath } from "url";
import UPLOAD_DIRECTORY_URL from "./config/UPLOAD_DIRECTORY_URL.mjs";
import schema from "./schema/index.mjs";
/**
* Starts the API server.
*/
/** Starts the API server. */
async function startServer() {
// Ensure the upload directory exists.
await makeDir(fileURLToPath(UPLOAD_DIRECTORY_URL));

View File

@ -1,3 +1,5 @@
// @ts-check
import { createWriteStream, unlink } from "fs";
import shortId from "shortid";
@ -5,7 +7,9 @@ import UPLOAD_DIRECTORY_URL from "./config/UPLOAD_DIRECTORY_URL.mjs";
/**
* Stores a GraphQL file upload in the filesystem.
* @param {Promise<object>} upload GraphQL file upload.
* @param {Promise<
* import("graphql-upload/processRequest.js").FileUpload
* >} upload GraphQL file upload.
* @returns {Promise<string>} Resolves the stored file name.
*/
export default async function storeUpload(upload) {

View File

@ -1,6 +1,14 @@
// @ts-check
import { createElement as h } from "react";
import styles from "./Header.module.css";
export const Header = (props) =>
h("header", { ...props, className: styles.header });
/**
* React component for a header.
* @param {object} props Props.
* @param {import("react").ReactNode} [props.children] Children.
*/
export default function Header({ children }) {
return h("header", { className: styles.header }, children);
}

View File

@ -1,11 +1,19 @@
import Head from "next/head";
import PropTypes from "prop-types";
// @ts-check
import nextHead from "next/head.js";
import { createElement as h, Fragment } from "react";
export const Page = ({ title, children }) =>
h(Fragment, null, h(Head, null, h("title", null, title)), children);
Page.propTypes = {
title: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
};
/**
* React component for a page.
* @param {object} props Props.
* @param {string} props.title Title.
* @param {import("react").ReactNode} [props.children] Children.
*/
export default function Page({ title, children }) {
return h(
Fragment,
null,
h(nextHead.default, null, h("title", null, title)),
children
);
}

View File

@ -1,6 +1,14 @@
// @ts-check
import { createElement as h } from "react";
import styles from "./Section.module.css";
export const Section = (props) =>
h("section", { ...props, className: styles.section });
/**
* React component for a section.
* @param {object} props Props.
* @param {import("react").ReactNode} [props.children] Children.
*/
export default function Section({ children }) {
return h("section", { className: styles.section }, children);
}

View File

@ -1,4 +1,8 @@
import { gql, useApolloClient, useMutation } from "@apollo/client";
// @ts-check
import { gql } from "@apollo/client/core";
import { useApolloClient } from "@apollo/client/react/hooks/useApolloClient.js";
import { useMutation } from "@apollo/client/react/hooks/useMutation.js";
import ButtonSubmit from "device-agnostic-ui/ButtonSubmit.mjs";
import Code from "device-agnostic-ui/Code.mjs";
import Fieldset from "device-agnostic-ui/Fieldset.mjs";
@ -13,7 +17,8 @@ const SINGLE_UPLOAD_MUTATION = gql`
}
`;
export function UploadBlob() {
/** React component for a uploading a blob. */
export default function UploadBlob() {
const [name, setName] = useState("");
const [content, setContent] = useState("");
const [singleUploadMutation, { loading }] = useMutation(
@ -21,18 +26,36 @@ export function UploadBlob() {
);
const apolloClient = useApolloClient();
const onNameChange = ({ target: { value } }) => setName(value);
const onContentChange = ({ target: { value } }) => setContent(value);
const onSubmit = (event) => {
/**
* @type {import("react").ChangeEventHandler<
* HTMLInputElement | HTMLTextAreaElement
* >}
*/
function onNameChange({ target: { value } }) {
setName(value);
}
/**
* @type {import("react").ChangeEventHandler<
* HTMLInputElement | HTMLTextAreaElement
* >}
*/
function onContentChange({ target: { value } }) {
setContent(value);
}
/** @type {import("react").FormEventHandler<HTMLFormElement>} */
function onSubmit(event) {
event.preventDefault();
const file = new Blob([content], { type: "text/plain" });
file.name = `${name}.txt`;
singleUploadMutation({ variables: { file } }).then(() => {
singleUploadMutation({
variables: {
file: new File([content], `${name}.txt`, { type: "text/plain" }),
},
}).then(() => {
apolloClient.resetStore();
});
};
}
return h(
"form",

View File

@ -1,4 +1,8 @@
import { gql, useApolloClient, useMutation } from "@apollo/client";
// @ts-check
import { gql } from "@apollo/client/core";
import { useApolloClient } from "@apollo/client/react/hooks/useApolloClient.js";
import { useMutation } from "@apollo/client/react/hooks/useMutation.js";
import { createElement as h } from "react";
const SINGLE_UPLOAD_MUTATION = gql`
@ -9,20 +13,18 @@ const SINGLE_UPLOAD_MUTATION = gql`
}
`;
export function UploadFile() {
/** React component for a uploading a single file. */
export default function UploadFile() {
const [uploadFileMutation] = useMutation(SINGLE_UPLOAD_MUTATION);
const apolloClient = useApolloClient();
const onChange = ({
target: {
validity,
files: [file],
},
}) =>
validity.valid &&
uploadFileMutation({ variables: { file } }).then(() => {
/** @type {import("react").ChangeEventHandler<HTMLInputElement>} */
function onChange({ target: { validity, files } }) {
if (validity.valid && files && files[0])
uploadFileMutation({ variables: { file: files[0] } }).then(() => {
apolloClient.resetStore();
});
}
return h("input", { type: "file", required: true, onChange });
}

View File

@ -1,4 +1,8 @@
import { gql, useApolloClient, useMutation } from "@apollo/client";
// @ts-check
import { gql } from "@apollo/client/core";
import { useApolloClient } from "@apollo/client/react/hooks/useApolloClient.js";
import { useMutation } from "@apollo/client/react/hooks/useMutation.js";
import { createElement as h } from "react";
const MULTIPLE_UPLOAD_MUTATION = gql`
@ -9,15 +13,33 @@ const MULTIPLE_UPLOAD_MUTATION = gql`
}
`;
export function UploadFileList() {
const [multipleUploadMutation] = useMutation(MULTIPLE_UPLOAD_MUTATION);
/**
* @typedef {{
* multipleUpload: {
* id: string,
* },
* }} MultipleUploadMutationData
*/
/** React component for a uploading a file list. */
export default function UploadFileList() {
const [multipleUploadMutation] =
/**
* @type {import("@apollo/client/react/types/types.js").MutationTuple<
* MultipleUploadMutationData,
* { files: FileList }
* >}
*/
(useMutation(MULTIPLE_UPLOAD_MUTATION));
const apolloClient = useApolloClient();
const onChange = ({ target: { validity, files } }) =>
validity.valid &&
/** @type {import("react").ChangeEventHandler<HTMLInputElement>} */
function onChange({ target: { validity, files } }) {
if (validity.valid && files && files[0])
multipleUploadMutation({ variables: { files } }).then(() => {
apolloClient.resetStore();
});
}
return h("input", { type: "file", multiple: true, required: true, onChange });
}

View File

@ -1,4 +1,7 @@
import { gql, useQuery } from "@apollo/client";
// @ts-check
import { gql } from "@apollo/client/core";
import { useQuery } from "@apollo/client/react/hooks/useQuery.js";
import Scroll from "device-agnostic-ui/Scroll.mjs";
import Table from "device-agnostic-ui/Table.mjs";
import { createElement as h } from "react";
@ -12,8 +15,24 @@ const UPLOADS_QUERY = gql`
}
`;
export function Uploads() {
const { data: { uploads = [] } = {} } = useQuery(UPLOADS_QUERY);
/**
* @typedef {{
* uploads: Array<{
* id: string,
* url: string
* }>,
* }} UploadsQueryData
*/
/** React component for displaying uploads. */
export default function Uploads() {
const { data: { uploads = [] } = {} } =
/**
* @type {import("@apollo/client/react/types/types.js").QueryResult<
* UploadsQueryData
* >}
*/
(useQuery(UPLOADS_QUERY));
return h(
Scroll,

11
app/jsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"maxNodeModuleJsDepth": 10,
"module": "nodenext",
"noEmit": true,
"strict": true
},
"typeAcquisition": {
"enable": false
}
}

View File

@ -1,3 +1,5 @@
// @ts-check
export default {
env: {
API_URI: process.env.API_URI,

116
app/package-lock.json generated
View File

@ -12,12 +12,14 @@
"device-agnostic-ui": "~10.0.0",
"graphql": "^16.5.0",
"next": "^12.1.6",
"prop-types": "^15.8.1",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@babel/eslint-parser": "^7.17.0",
"@types/node": "^17.0.35",
"@types/react": "^17.0.45",
"@types/react-dom": "^17.0.17",
"babel-plugin-graphql-tag": "^3.3.0",
"eslint": "^8.16.0",
"eslint-plugin-react-hooks": "^4.5.0",
@ -25,10 +27,11 @@
"prettier": "^2.6.2",
"stylelint": "^14.8.3",
"stylelint-config-recommended": "^7.0.0",
"stylelint-prettier": "^2.0.0"
"stylelint-prettier": "^2.0.0",
"typescript": "^4.7.1-rc"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >= 16.0.0",
"node": "^14.17.0 || ^16.0.0 || >= 18.0.0",
"npm": ">= 7"
},
"funding": {
@ -764,6 +767,12 @@
"integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==",
"dev": true
},
"node_modules/@types/node": {
"version": "17.0.35",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.35.tgz",
"integrity": "sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg==",
"dev": true
},
"node_modules/@types/normalize-package-data": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
@ -776,6 +785,38 @@
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
},
"node_modules/@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true
},
"node_modules/@types/react": {
"version": "17.0.45",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.45.tgz",
"integrity": "sha512-YfhQ22Lah2e3CHPsb93tRwIGNiSwkuz1/blk4e6QrWS0jQzCSNbGLtOEYhPg02W0yGTTmpajp7dCTbBAMN3qsg==",
"dev": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-dom": {
"version": "17.0.17",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.17.tgz",
"integrity": "sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==",
"dev": true,
"dependencies": {
"@types/react": "^17"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
"dev": true
},
"node_modules/@wry/context": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/@wry/context/-/context-0.6.1.tgz",
@ -1181,6 +1222,12 @@
"node": ">=4"
}
},
"node_modules/csstype": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
"integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
"dev": true
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -3637,6 +3684,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/typescript": {
"version": "4.7.1-rc",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.1-rc.tgz",
"integrity": "sha512-EQd2NVelDe6ZVc2sO1CSpuSs+RHzY8c2n/kTNQAHw4um/eAXY+ZY4IKoUpNK0wO6C5hN+XcUXR7yqT8VbwwNIQ==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@ -4234,6 +4294,12 @@
"integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==",
"dev": true
},
"@types/node": {
"version": "17.0.35",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.35.tgz",
"integrity": "sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg==",
"dev": true
},
"@types/normalize-package-data": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
@ -4246,6 +4312,38 @@
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
},
"@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true
},
"@types/react": {
"version": "17.0.45",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.45.tgz",
"integrity": "sha512-YfhQ22Lah2e3CHPsb93tRwIGNiSwkuz1/blk4e6QrWS0jQzCSNbGLtOEYhPg02W0yGTTmpajp7dCTbBAMN3qsg==",
"dev": true,
"requires": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"@types/react-dom": {
"version": "17.0.17",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.17.tgz",
"integrity": "sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==",
"dev": true,
"requires": {
"@types/react": "^17"
}
},
"@types/scheduler": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
"dev": true
},
"@wry/context": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/@wry/context/-/context-0.6.1.tgz",
@ -4530,6 +4628,12 @@
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true
},
"csstype": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
"integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
"dev": true
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -6316,6 +6420,12 @@
"integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
"dev": true
},
"typescript": {
"version": "4.7.1-rc",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.1-rc.tgz",
"integrity": "sha512-EQd2NVelDe6ZVc2sO1CSpuSs+RHzY8c2n/kTNQAHw4um/eAXY+ZY4IKoUpNK0wO6C5hN+XcUXR7yqT8VbwwNIQ==",
"dev": true
},
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",

View File

@ -16,22 +16,24 @@
"bugs": "https://github.com/jaydenseric/apollo-upload-examples/issues",
"funding": "https://github.com/sponsors/jaydenseric",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >= 16.0.0",
"node": "^14.17.0 || ^16.0.0 || >= 18.0.0",
"npm": ">= 7"
},
"browserslist": "Node 12.22 - 13 and Node < 13, Node 14.17 - 15 and Node < 15, Node >= 16, > 0.5%, not OperaMini all, not IE > 0, not dead",
"browserslist": "Node 14.17 - 15 and Node < 15, Node 16 - 17 and Node < 17, Node >= 18, > 0.5%, not OperaMini all, not IE > 0, not dead",
"dependencies": {
"@apollo/client": "^3.6.4",
"apollo-upload-client": "^17.0.0",
"device-agnostic-ui": "~10.0.0",
"graphql": "^16.5.0",
"next": "^12.1.6",
"prop-types": "^15.8.1",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@babel/eslint-parser": "^7.17.0",
"@types/node": "^17.0.35",
"@types/react": "^17.0.45",
"@types/react-dom": "^17.0.17",
"babel-plugin-graphql-tag": "^3.3.0",
"eslint": "^8.16.0",
"eslint-plugin-react-hooks": "^4.5.0",
@ -39,7 +41,8 @@
"prettier": "^2.6.2",
"stylelint": "^14.8.3",
"stylelint-config-recommended": "^7.0.0",
"stylelint-prettier": "^2.0.0"
"stylelint-prettier": "^2.0.0",
"typescript": "^4.7.1-rc"
},
"scripts": {
"dev": "next dev",
@ -48,6 +51,7 @@
"eslint": "eslint .",
"stylelint": "stylelint '**/*.css'",
"prettier": "prettier -c .",
"test": "npm run eslint && npm run stylelint && npm run prettier"
"types": "tsc -p jsconfig.json",
"test": "npm run eslint && npm run stylelint && npm run prettier && npm run types"
}
}

View File

@ -1,3 +1,5 @@
// @ts-check
import "device-agnostic-ui/theme.css";
import "device-agnostic-ui/global.css";
import "device-agnostic-ui/Button.css";
@ -11,12 +13,18 @@ import "device-agnostic-ui/Scroll.css";
import "device-agnostic-ui/Table.css";
import "device-agnostic-ui/Textbox.css";
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";
import { InMemoryCache } from "@apollo/client/cache/inmemory/inMemoryCache.js";
import { ApolloClient } from "@apollo/client/core/ApolloClient.js";
import { ApolloProvider } from "@apollo/client/react/context/ApolloProvider.js";
import { createUploadLink } from "apollo-upload-client";
import Head from "next/head";
import PropTypes from "prop-types";
import { createElement as h } from "react";
import nextApp from "next/app.js";
import nextHead from "next/head.js";
import { createElement as h, Fragment } from "react";
/**
* Creates an Apollo Client instance.
* @param {{ [key: string]: unknown }} [cache] Apollo Client initial cache.
*/
const createApolloClient = (cache = {}) =>
new ApolloClient({
ssrMode: typeof window === "undefined",
@ -24,17 +32,23 @@ const createApolloClient = (cache = {}) =>
link: createUploadLink({ uri: process.env.API_URI }),
});
const App = ({
/**
* React component for the Next.js app.
* @param {import("next/app.js").AppProps & AppCustomProps} props Props.
*/
function App({
Component,
pageProps,
apolloCache,
apolloClient = createApolloClient(apolloCache),
}) =>
}) {
return h(ApolloProvider, {
client: apolloClient,
children: h(
Fragment,
null,
h(
ApolloProvider,
{ client: apolloClient },
h(
Head,
nextHead.default,
null,
h("meta", {
name: "viewport",
@ -45,43 +59,48 @@ const App = ({
h("link", { rel: "manifest", href: "/manifest.webmanifest" })
),
h(Component, pageProps)
);
),
});
}
App.getInitialProps = async (context) => {
const props = {
pageProps: context.Component.getInitialProps
? await context.Component.getInitialProps(context)
: {},
};
if (context.ctx.req) {
if (typeof window === "undefined")
App.getInitialProps =
/** @param {import("next/app.js").AppContext} context */
async function getInitialProps(context) {
const apolloClient = createApolloClient();
const [props, { default: ReactDOMServer }, { getMarkupFromTree }] =
await Promise.all([
nextApp.default.getInitialProps(context),
import("react-dom/server.js"),
import("@apollo/client/react/ssr/getDataFromTree.js"),
]);
try {
const { getDataFromTree } = await import("@apollo/client/react/ssr");
await getDataFromTree(
h(App, {
await getMarkupFromTree({
tree: h(App, {
...props,
apolloClient,
router: context.router,
Component: context.Component,
})
);
}),
renderFunction: ReactDOMServer.renderToStaticMarkup,
});
} catch (error) {
// Prevent crash from GraphQL errors.
console.error(error);
}
props.apolloCache = apolloClient.cache.extract();
}
return props;
};
App.propTypes = {
Component: PropTypes.elementType.isRequired,
pageProps: PropTypes.object,
apolloCache: PropTypes.object,
apolloClient: PropTypes.instanceOf(ApolloClient),
};
return {
...props,
apolloCache: apolloClient.cache.extract(),
};
};
export default App;
/**
* Next.js app custom props.
* @typedef {object} AppCustomProps
* @prop {{ [key: string]: unknown }} [apolloCache] Apollo Client initial cache.
* @prop {ApolloClient<any>} apolloClient Apollo Client.
*/

View File

@ -1,15 +1,17 @@
// @ts-check
import Code from "device-agnostic-ui/Code.mjs";
import Heading from "device-agnostic-ui/Heading.mjs";
import Margin from "device-agnostic-ui/Margin.mjs";
import { createElement as h } from "react";
import { Header } from "../components/Header.mjs";
import { Page } from "../components/Page.mjs";
import { Section } from "../components/Section.mjs";
import { UploadBlob } from "../components/UploadBlob.mjs";
import { UploadFile } from "../components/UploadFile.mjs";
import { UploadFileList } from "../components/UploadFileList.mjs";
import { Uploads } from "../components/Uploads.mjs";
import Header from "../components/Header.mjs";
import Page from "../components/Page.mjs";
import Section from "../components/Section.mjs";
import UploadBlob from "../components/UploadBlob.mjs";
import UploadFile from "../components/UploadFile.mjs";
import UploadFileList from "../components/UploadFileList.mjs";
import Uploads from "../components/Uploads.mjs";
export default function IndexPage() {
return h(

4
app/typings.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module "*.module.css" {
const classes: { [key: string]: string };
export default classes;
}