-
Notifications
You must be signed in to change notification settings - Fork 144
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
Migrate wasm-themis to TypeScript #792
Conversation
That way we can use the TS default values in function calls
I'm not sure where to change example or where examples exist. The api only changed ìnitialised |
@@ -0,0 +1,236 @@ | |||
import { ThemisErrorCode } from "./themis_error"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file was crafted manually and describes the type of libthemis.js
} | ||
|
||
encrypt(message: Uint8Array, context: Uint8Array) { | ||
message = coerceToBytes(message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a future PR I would like to remove all usages of coerceToBytes
and demand a Uint8Array to be passed to the library. That would simplify the code in many places.
@@ -12,7 +12,7 @@ | |||
// See the License for the specific language governing permissions and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left test.js as is. That way I could verify that I did not break anything.
@@ -36,14 +36,14 @@ function measureTime(thunk) { | |||
|
|||
describe('wasm-themis', function() { | |||
let generallyInvalidArguments = [ | |||
null, undefined, 'string', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undefined
means no argument in TS and ES6. Therefore it is an valid argument.
@@ -26,6 +26,7 @@ WASM_PACKAGE = $(BIN_PATH)/wasm-themis.tgz | |||
|
|||
$(BIN_PATH)/libthemis.js: LDFLAGS += -s EXTRA_EXPORTED_RUNTIME_METHODS=@$(WASM_RUNTIME) | |||
$(BIN_PATH)/libthemis.js: LDFLAGS += -s ALLOW_TABLE_GROWTH | |||
$(BIN_PATH)/libthemis.js: LDFLAGS += -s MODULARIZE=1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is needed for web. This allows me to configure the path to the wasm file.
I think the RustThemis failure is not introduced in this PR. For some reason Node 10 fails because of a timeout when initializing wasm. Not sure why this happens... |
export class PrivateKey extends Uint8Array { | ||
export class PrivateKey { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to remove the extends. Officially it is not supported in ES5 to extend buildins. I want to target ES5 instead of ES6 by default to be compatible with the majority of browsers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This led to some minor changes in tests.
if (error instanceof themis.ThemisError) { | ||
if (error.errorCode) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instanceof is also not supported in ES5. Currently tests run with ES5 to catch comparability issues.
Next steps:
|
Wow, that's a lot 🔥 |
Yeah I tried to do minimal changes. But a rewrite is a rewrite :D The other changes can follow in other prs. They are optional and just code quality improvements. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, I'm hugely positive for this changeset. More type-level static guarantees with acceptable usability is better. I'm especially happy that someone actually experienced with JavaScript gave at look at it. I know how tedious it might have been, so props from me, Max! ❤️
export const initialize = async (wasmPath: string) => { | ||
const libthemis = await libthemisFn({ | ||
onRuntimeInitialized: function () {}, | ||
locateFile: function () { | ||
return wasmPath; | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the formatting change from "no semicolons and 4-space indents with single quotes" to "use semicolons with 2-space indents and double quotes". Most of the codebase uses 4-space indents, unless there is a strong preference for a particular style in the language (e.g., tabs in Go, 2-space indents in Ruby). Is there such thing in TypeScript ecosystem as a whole?
I don't really mind the style changing, given that all these lines are still going to change anyway with added typing and strict checks. But it would be nice to make it less of a cowboy and more organized. It would be cool to throw in code formatter to ensure that all newly added code is consistent with the new style. (Not in this PR. But maybe some time later.)
(Admittedly, existing JavaScript code is a mess right now. JsThemis tests, for example, use 2, 4, 8-space indents and a bunch of tabs in different places.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, I already throught about throwing in prettier.
Pretter works across IDEs and is the defacto standard for formating. I add this to the todo list.
"@types/emscripten": "^1.39.4", | ||
"@types/node": "^14.14.36", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How strictly those should track the version of Emscripten used to compile WasmThemis? (System Node.js is less important since Emscripten uses its own, IIRC.)
Emscripten APIs don't change often, but recently they did. I guess we should just try updating those from time to time, see if it builds and we're good?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should change the version of the types if we upgrade emscripten. I am not sure who creates the types but i hope it is somebody who is involved in the emscripten project.
The cool thing is, if we upgrade types here and there were api changes, then we will get compile time errors.
describe('initialization', function() { | ||
it('resolves "initialized" promise', function(done) { | ||
themis.initialized.then(function() { | ||
themis.initialize().then(function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vixentael, how strict are we on backwards compatibility in WasmThemis? Does the usual “we do not break userspace” apply here as well?
In my opinion, unless it's unbearable and makes maintenance a hell, Themis should strive to stick to once exported APIs and avoid breakage. From what I have seen so far, there are the following changes:
-
Most APIs now accept strictly
Uint8Array
, disallowingArrayBuffer
(in TypeScript).Overall, I'm okay with making input types more strict – don't really remember the story behind
coerceToBytes()
. The tests don't look like they test this behavior either.Since there was no TypeScript support before, TypeScript users will just be using Uint8Array everywhere from the day 1.
Vanilla JavaScript users will not be affected by this for now: existing
coerceToBytes()
calls will check types at runtime and convertArrayBuffer
intoUint8Array
. TypeScript won't insert extra runtime checks that would block that. However, oncecoerceToBytes()
calls are gone, usingArrayBuffer
will become an error.@maxammann, is my reasoning correct so far?
If so, I guess it would be nice to add some runtime warning when
ArrayBuffer
is passed to WasmThemis API, notifying JavaScript users that support for this will soon be removed, please cast toUint8Array
yourself. -
initialized
promise =>initialize()
async function.Ideally, I'd like to get rid of this thing entirely as it serves no useful purpose other than making a feeble sacrifice to the God of async. But I guess not today... I'm not really sure there is a better way other than inserting a check & wait for initialization into every exported public function. Or providing an entirely new async API.
However, I believe it's not be nice to snatch the rag from existing users and remove
initialized
entirely. Would it be possible forinitialized
andinitialize()
to coexist for a while? -
Changing the base type of private/public keys.
Now they are not
Uint8Array
by themselves, you should use the.data
field to access key date. This might break user code that stores, encrypts, and otherwise processes keys.Is there maybe some way to keep inheriting from
Uint8Array
for ES6 code, but provide adata
accessor for ES5 compatibility? (I believe WasmThemis supported only ES6 before...)
(And if we're going to make any breaking changes, they should be communicated and documented in the changelog along with migration path.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maxammann, is my reasoning correct so far?
Perfectly correct!
However, I believe it's not be nice to snatch the rag from existing users and remove
initialized
entirely. Would it be possible forinitialized
andinitialize()
to coexist for a while?
I will wee whether that is possible. I can not start loading themis as soon as this library is importet as you may want to configure the path to the wasm binary (required for web and webpack).
- Changing the base type of private/public keys.
Supporting es5 is not possible with that extends syntax unfortunately. I had to support and target es5 such that the integration CI passes. So I think we should support it. The reason why it worked before is that without a compilation it somehow worked as node supports a mix between es5 and es6. Some browsers do not and only support es5. Therefore this kind of fixed bugs for users of older browsers.
BUT: We could drop support for ES5 actually because every browser which supports webassambly should already support ES6. This is also the reason why we do not need to support IE11. Webassambly does not work there in the first place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfectly correct!
Whew 😌 Thanks for confirming.
I will wee whether that is possible. I can not start loading themis as soon as this library is importet as you may want to configure the path to the wasm binary (required for web and webpack).
We have not really considered – and tested – the web usage. Knows users of WasmThemis are all on Node.js.
I remember doing some manual tests and that it kinda worked. But I'm no web developer so I believe no one so far has tested WasmThemis with proper web apps. Hence, I wouldn't be really surprised that it does not work out of the box. If something is necessary for proper web support – go ahead, you have my blessing to add whatever is needed 👍
It would be nice to keep the initialized
promise around – I'm sorry, I don't have any ideas right now – but it it's not possible, then, well, WasmThemis users are getting a flag day. This one should not be too painful to work around.
BUT: We could drop support for ES5 actually because every browser which supports webassambly should already support ES6.
Uh-huh... I faintly remember this reasoning behind setting the minimal supported version to ES6 and using all those fancy things.
Personally, I'm fine with ES5 support being added, but probably not at an expense of backwards-incompatible changes to the API which are caused by ES5 lack of features. If there was a way to give ES5 users one code and ES6 gets a different one – that would be great, but browsers don't work like that: it's easier to serve the same version to everyone without trying to guess what their browser might or might not support. So the approach would be something like "use some magic transpiler to convert ES6 code into ES5-compatible code".
Also, a comment from the operational point of view for @shadinua: WasmThemis publishing should not really change much with all of this. You can still just run |
FYI, @maxammann, the fix for Rust build has been merged into master, so you can pull the updated version and merge it into your branch to turn the build green. Just a merge commit with |
@ilammy any update on this one? :) |
@maxamman, I'm sorry, it seems that high-context culture has some effects on me and I kinda mishandled this. It looks like we ended up playing this silly game where everybody expects other people to do something, but they never tell that explicitly and the other party does not read the air. And so weeks pass without anyone doing anything while everybody is under impression something is being done and it just takes time. From my point of view, open-source contributors are volunteers and all, they have other stuff to keep them busy. From your point of view, I think, maintainers might look the same. I'm biased for merging this to make future work easier. However, this will block the 0.17.0 release until the backwards compatibility issues outlined in #792 (comment) are resolved (one way or another: either we avoid them, or we assert them). I'm not aware of time constraints for this release, so I guess I'd go for making it when it's done rather than postponing TypeScript rework for some later release. @vixentael, what's your take on this? Again, @maxammann, I'm sorry for not setting my expectations straight 🙏 I was under impression that you were looking into those issues, it just takes time. |
Absolutely no problem, no mishandling happened in my opinion :) Actually, I did not see your last comment in our discussion. As far as I can see I have two open things:
|
Thank you for your patience @maxammann! Scenario 1
Would be nice if we can!
I'm ok with this., it's old :) If we can support old Scenario 2If we can't support old I'd love to support backwards compatibility, but I understand that WASM Themis is typically used in desktop and web apps, which move fast. Soo... sorry devs? We will describe breaking changes as detailed as possible. @maxammann do you think it's possible to support |
Alrighty. @maxammann, I've retargeted your PR onto a new branch – This way if Themis really needs a release, we can provide it without potentially introducing a breaking change. Hopefully, we won't need this and TypeScript will be in there in Themis 0.14. |
Thanks! Right now I don't have time to continue but soon I will :) Just ping me when there are urgent changes for wasm-themis. |
So my PR for TS is finally here. This PR only changes syntax and should not change semantics. This is not possible in every case, but in all cases that matter.
The module works in node, older browsers and also works using ES6 and TypeScript. Here is an example for node:
Example for web like a react app:
Here are links to the diffs for easier reviewing:
You can simple review the commit referenced above and then only review the changes to the package.json and build files.
Checklist