[wip] packaging nacl
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{type, site}.
|
||||
{realm, local}.
|
||||
{name, jrw}.
|
||||
{version, "0.1.0"}.
|
||||
{deps, ["local-awcp-0.2.0"]}.
|
||||
@@ -0,0 +1,2 @@
|
||||
docs
|
||||
erl_crash.dump
|
||||
@@ -0,0 +1,482 @@
|
||||
TweetNaCl.js
|
||||
============
|
||||
|
||||
Port of [TweetNaCl](http://tweetnacl.cr.yp.to) / [NaCl](http://nacl.cr.yp.to/)
|
||||
to JavaScript for modern browsers and Node.js. Public domain.
|
||||
|
||||
[
|
||||
](https://travis-ci.org/dchest/tweetnacl-js)
|
||||
|
||||
Demo: <https://dchest.github.io/tweetnacl-js/>
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
* [Overview](#overview)
|
||||
* [Audits](#audits)
|
||||
* [Installation](#installation)
|
||||
* [Examples](#examples)
|
||||
* [Usage](#usage)
|
||||
* [Public-key authenticated encryption (box)](#public-key-authenticated-encryption-box)
|
||||
* [Secret-key authenticated encryption (secretbox)](#secret-key-authenticated-encryption-secretbox)
|
||||
* [Scalar multiplication](#scalar-multiplication)
|
||||
* [Signatures](#signatures)
|
||||
* [Hashing](#hashing)
|
||||
* [Random bytes generation](#random-bytes-generation)
|
||||
* [Constant-time comparison](#constant-time-comparison)
|
||||
* [System requirements](#system-requirements)
|
||||
* [Development and testing](#development-and-testing)
|
||||
* [Benchmarks](#benchmarks)
|
||||
* [Contributors](#contributors)
|
||||
* [Who uses it](#who-uses-it)
|
||||
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The primary goal of this project is to produce a translation of TweetNaCl to
|
||||
JavaScript which is as close as possible to the original C implementation, plus
|
||||
a thin layer of idiomatic high-level API on top of it.
|
||||
|
||||
There are two versions, you can use either of them:
|
||||
|
||||
* `nacl.js` is the port of TweetNaCl with minimum differences from the
|
||||
original + high-level API.
|
||||
|
||||
* `nacl-fast.js` is like `nacl.js`, but with some functions replaced with
|
||||
faster versions. (Used by default when importing NPM package.)
|
||||
|
||||
|
||||
Audits
|
||||
------
|
||||
|
||||
TweetNaCl.js has been audited by [Cure53](https://cure53.de/) in January-February
|
||||
2017 (audit was sponsored by [Deletype](https://deletype.com)):
|
||||
|
||||
> The overall outcome of this audit signals a particularly positive assessment
|
||||
> for TweetNaCl-js, as the testing team was unable to find any security
|
||||
> problems in the library.
|
||||
|
||||
[Read full audit report](https://cure53.de/tweetnacl.pdf)
|
||||
|
||||
While the audit didn't find any bugs, there has been [1 bug](https://github.com/dchest/tweetnacl-js/issues/187) discovered and fixed after the audit.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
You can install TweetNaCl.js via a package manager:
|
||||
|
||||
[Yarn](https://yarnpkg.com/):
|
||||
|
||||
$ yarn add tweetnacl
|
||||
|
||||
[NPM](https://www.npmjs.org/):
|
||||
|
||||
$ npm install tweetnacl
|
||||
|
||||
or [download source code](https://github.com/dchest/tweetnacl-js/releases).
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
You can find usage examples in our [wiki](https://github.com/dchest/tweetnacl-js/wiki/Examples).
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
All API functions accept and return bytes as `Uint8Array`s. If you need to
|
||||
encode or decode strings, use functions from
|
||||
<https://github.com/dchest/tweetnacl-util-js> or one of the more robust codec
|
||||
packages.
|
||||
|
||||
In Node.js v4 and later `Buffer` objects are backed by `Uint8Array`s, so you
|
||||
can freely pass them to TweetNaCl.js functions as arguments. The returned
|
||||
objects are still `Uint8Array`s, so if you need `Buffer`s, you'll have to
|
||||
convert them manually; make sure to convert using copying: `Buffer.from(array)`
|
||||
(or `new Buffer(array)` in Node.js v4 or earlier), instead of sharing:
|
||||
`Buffer.from(array.buffer)` (or `new Buffer(array.buffer)` Node 4 or earlier),
|
||||
because some functions return subarrays of their buffers.
|
||||
|
||||
|
||||
### Public-key authenticated encryption (box)
|
||||
|
||||
Implements *x25519-xsalsa20-poly1305*.
|
||||
|
||||
#### nacl.box.keyPair()
|
||||
|
||||
Generates a new random key pair for box and returns it as an object with
|
||||
`publicKey` and `secretKey` members:
|
||||
|
||||
{
|
||||
publicKey: ..., // Uint8Array with 32-byte public key
|
||||
secretKey: ... // Uint8Array with 32-byte secret key
|
||||
}
|
||||
|
||||
|
||||
#### nacl.box.keyPair.fromSecretKey(secretKey)
|
||||
|
||||
Returns a key pair for box with public key corresponding to the given secret
|
||||
key.
|
||||
|
||||
#### nacl.box(message, nonce, theirPublicKey, mySecretKey)
|
||||
|
||||
Encrypts and authenticates message using peer's public key, our secret key, and
|
||||
the given nonce, which must be unique for each distinct message for a key pair.
|
||||
|
||||
Returns an encrypted and authenticated message, which is
|
||||
`nacl.box.overheadLength` longer than the original message.
|
||||
|
||||
#### nacl.box.open(box, nonce, theirPublicKey, mySecretKey)
|
||||
|
||||
Authenticates and decrypts the given box with peer's public key, our secret
|
||||
key, and the given nonce.
|
||||
|
||||
Returns the original message, or `null` if authentication fails.
|
||||
|
||||
#### nacl.box.before(theirPublicKey, mySecretKey)
|
||||
|
||||
Returns a precomputed shared key which can be used in `nacl.box.after` and
|
||||
`nacl.box.open.after`.
|
||||
|
||||
#### nacl.box.after(message, nonce, sharedKey)
|
||||
|
||||
Same as `nacl.box`, but uses a shared key precomputed with `nacl.box.before`.
|
||||
|
||||
#### nacl.box.open.after(box, nonce, sharedKey)
|
||||
|
||||
Same as `nacl.box.open`, but uses a shared key precomputed with `nacl.box.before`.
|
||||
|
||||
#### Constants
|
||||
|
||||
##### nacl.box.publicKeyLength = 32
|
||||
|
||||
Length of public key in bytes.
|
||||
|
||||
##### nacl.box.secretKeyLength = 32
|
||||
|
||||
Length of secret key in bytes.
|
||||
|
||||
##### nacl.box.sharedKeyLength = 32
|
||||
|
||||
Length of precomputed shared key in bytes.
|
||||
|
||||
##### nacl.box.nonceLength = 24
|
||||
|
||||
Length of nonce in bytes.
|
||||
|
||||
##### nacl.box.overheadLength = 16
|
||||
|
||||
Length of overhead added to box compared to original message.
|
||||
|
||||
|
||||
### Secret-key authenticated encryption (secretbox)
|
||||
|
||||
Implements *xsalsa20-poly1305*.
|
||||
|
||||
#### nacl.secretbox(message, nonce, key)
|
||||
|
||||
Encrypts and authenticates message using the key and the nonce. The nonce must
|
||||
be unique for each distinct message for this key.
|
||||
|
||||
Returns an encrypted and authenticated message, which is
|
||||
`nacl.secretbox.overheadLength` longer than the original message.
|
||||
|
||||
#### nacl.secretbox.open(box, nonce, key)
|
||||
|
||||
Authenticates and decrypts the given secret box using the key and the nonce.
|
||||
|
||||
Returns the original message, or `null` if authentication fails.
|
||||
|
||||
#### Constants
|
||||
|
||||
##### nacl.secretbox.keyLength = 32
|
||||
|
||||
Length of key in bytes.
|
||||
|
||||
##### nacl.secretbox.nonceLength = 24
|
||||
|
||||
Length of nonce in bytes.
|
||||
|
||||
##### nacl.secretbox.overheadLength = 16
|
||||
|
||||
Length of overhead added to secret box compared to original message.
|
||||
|
||||
|
||||
### Scalar multiplication
|
||||
|
||||
Implements *x25519*.
|
||||
|
||||
#### nacl.scalarMult(n, p)
|
||||
|
||||
Multiplies an integer `n` by a group element `p` and returns the resulting
|
||||
group element.
|
||||
|
||||
#### nacl.scalarMult.base(n)
|
||||
|
||||
Multiplies an integer `n` by a standard group element and returns the resulting
|
||||
group element.
|
||||
|
||||
#### Constants
|
||||
|
||||
##### nacl.scalarMult.scalarLength = 32
|
||||
|
||||
Length of scalar in bytes.
|
||||
|
||||
##### nacl.scalarMult.groupElementLength = 32
|
||||
|
||||
Length of group element in bytes.
|
||||
|
||||
|
||||
### Signatures
|
||||
|
||||
Implements [ed25519](http://ed25519.cr.yp.to).
|
||||
|
||||
#### nacl.sign.keyPair()
|
||||
|
||||
Generates new random key pair for signing and returns it as an object with
|
||||
`publicKey` and `secretKey` members:
|
||||
|
||||
{
|
||||
publicKey: ..., // Uint8Array with 32-byte public key
|
||||
secretKey: ... // Uint8Array with 64-byte secret key
|
||||
}
|
||||
|
||||
#### nacl.sign.keyPair.fromSecretKey(secretKey)
|
||||
|
||||
Returns a signing key pair with public key corresponding to the given
|
||||
64-byte secret key. The secret key must have been generated by
|
||||
`nacl.sign.keyPair` or `nacl.sign.keyPair.fromSeed`.
|
||||
|
||||
#### nacl.sign.keyPair.fromSeed(seed)
|
||||
|
||||
Returns a new signing key pair generated deterministically from a 32-byte seed.
|
||||
The seed must contain enough entropy to be secure. This method is not
|
||||
recommended for general use: instead, use `nacl.sign.keyPair` to generate a new
|
||||
key pair from a random seed.
|
||||
|
||||
#### nacl.sign(message, secretKey)
|
||||
|
||||
Signs the message using the secret key and returns a signed message.
|
||||
|
||||
#### nacl.sign.open(signedMessage, publicKey)
|
||||
|
||||
Verifies the signed message and returns the message without signature.
|
||||
|
||||
Returns `null` if verification failed.
|
||||
|
||||
#### nacl.sign.detached(message, secretKey)
|
||||
|
||||
Signs the message using the secret key and returns a signature.
|
||||
|
||||
#### nacl.sign.detached.verify(message, signature, publicKey)
|
||||
|
||||
Verifies the signature for the message and returns `true` if verification
|
||||
succeeded or `false` if it failed.
|
||||
|
||||
#### Constants
|
||||
|
||||
##### nacl.sign.publicKeyLength = 32
|
||||
|
||||
Length of signing public key in bytes.
|
||||
|
||||
##### nacl.sign.secretKeyLength = 64
|
||||
|
||||
Length of signing secret key in bytes.
|
||||
|
||||
##### nacl.sign.seedLength = 32
|
||||
|
||||
Length of seed for `nacl.sign.keyPair.fromSeed` in bytes.
|
||||
|
||||
##### nacl.sign.signatureLength = 64
|
||||
|
||||
Length of signature in bytes.
|
||||
|
||||
|
||||
### Hashing
|
||||
|
||||
Implements *SHA-512*.
|
||||
|
||||
#### nacl.hash(message)
|
||||
|
||||
Returns SHA-512 hash of the message.
|
||||
|
||||
#### Constants
|
||||
|
||||
##### nacl.hash.hashLength = 64
|
||||
|
||||
Length of hash in bytes.
|
||||
|
||||
|
||||
### Random bytes generation
|
||||
|
||||
#### nacl.randomBytes(length)
|
||||
|
||||
Returns a `Uint8Array` of the given length containing random bytes of
|
||||
cryptographic quality.
|
||||
|
||||
**Implementation note**
|
||||
|
||||
TweetNaCl.js uses the following methods to generate random bytes,
|
||||
depending on the platform it runs on:
|
||||
|
||||
* `window.crypto.getRandomValues` (WebCrypto standard)
|
||||
* `window.msCrypto.getRandomValues` (Internet Explorer 11)
|
||||
* `crypto.randomBytes` (Node.js)
|
||||
|
||||
If the platform doesn't provide a suitable PRNG, the following functions,
|
||||
which require random numbers, will throw exception:
|
||||
|
||||
* `nacl.randomBytes`
|
||||
* `nacl.box.keyPair`
|
||||
* `nacl.sign.keyPair`
|
||||
|
||||
Other functions are deterministic and will continue working.
|
||||
|
||||
If a platform you are targeting doesn't implement secure random number
|
||||
generator, but you somehow have a cryptographically-strong source of entropy
|
||||
(not `Math.random`!), and you know what you are doing, you can plug it into
|
||||
TweetNaCl.js like this:
|
||||
|
||||
nacl.setPRNG(function(x, n) {
|
||||
// ... copy n random bytes into x ...
|
||||
});
|
||||
|
||||
Note that `nacl.setPRNG` *completely replaces* internal random byte generator
|
||||
with the one provided.
|
||||
|
||||
|
||||
### Constant-time comparison
|
||||
|
||||
#### nacl.verify(x, y)
|
||||
|
||||
Compares `x` and `y` in constant time and returns `true` if their lengths are
|
||||
non-zero and equal, and their contents are equal.
|
||||
|
||||
Returns `false` if either of the arguments has zero length, or arguments have
|
||||
different lengths, or their contents differ.
|
||||
|
||||
|
||||
System requirements
|
||||
-------------------
|
||||
|
||||
TweetNaCl.js supports modern browsers that have a cryptographically secure
|
||||
pseudorandom number generator and typed arrays, including the latest versions
|
||||
of:
|
||||
|
||||
* Chrome
|
||||
* Firefox
|
||||
* Safari (Mac, iOS)
|
||||
* Internet Explorer 11
|
||||
|
||||
Other systems:
|
||||
|
||||
* Node.js
|
||||
|
||||
|
||||
Development and testing
|
||||
------------------------
|
||||
|
||||
Install NPM modules needed for development:
|
||||
|
||||
$ npm install
|
||||
|
||||
To build minified versions:
|
||||
|
||||
$ npm run build
|
||||
|
||||
Tests use minified version, so make sure to rebuild it every time you change
|
||||
`nacl.js` or `nacl-fast.js`.
|
||||
|
||||
### Testing
|
||||
|
||||
To run tests in Node.js:
|
||||
|
||||
$ npm run test-node
|
||||
|
||||
By default all tests described here work on `nacl.min.js`. To test other
|
||||
versions, set environment variable `NACL_SRC` to the file name you want to test.
|
||||
For example, the following command will test fast minified version:
|
||||
|
||||
$ NACL_SRC=nacl-fast.min.js npm run test-node
|
||||
|
||||
To run full suite of tests in Node.js, including comparing outputs of
|
||||
JavaScript port to outputs of the original C version:
|
||||
|
||||
$ npm run test-node-all
|
||||
|
||||
To prepare tests for browsers:
|
||||
|
||||
$ npm run build-test-browser
|
||||
|
||||
and then open `test/browser/test.html` (or `test/browser/test-fast.html`) to
|
||||
run them.
|
||||
|
||||
To run tests in both Node and Electron:
|
||||
|
||||
$ npm test
|
||||
|
||||
### Benchmarking
|
||||
|
||||
To run benchmarks in Node.js:
|
||||
|
||||
$ npm run bench
|
||||
$ NACL_SRC=nacl-fast.min.js npm run bench
|
||||
|
||||
To run benchmarks in a browser, open `test/benchmark/bench.html` (or
|
||||
`test/benchmark/bench-fast.html`).
|
||||
|
||||
|
||||
Benchmarks
|
||||
----------
|
||||
|
||||
For reference, here are benchmarks from MacBook Pro (Retina, 13-inch, Mid 2014)
|
||||
laptop with 2.6 GHz Intel Core i5 CPU (Intel) in Chrome 53/OS X, Xiaomi Redmi
|
||||
Note 3 smartphone with 1.8 GHz Qualcomm Snapdragon 650 64-bit CPU (ARM) in
|
||||
Chrome 52/Android, and MacBook Air 2020 with Apple M1 SOC (M1) in Chromium 102/macOS.
|
||||
|
||||
| | nacl.js Intel | nacl-fast.js Intel | nacl.js ARM | nacl-fast.js ARM | nacl-fast.js M1 |
|
||||
| ------------- |:-------------:|:-------------------:|:-------------:|:-----------------:|:-----------------:|
|
||||
| salsa20 | 1.3 MB/s | 128 MB/s | 0.4 MB/s | 43 MB/s | 268 MB/s |
|
||||
| poly1305 | 13 MB/s | 171 MB/s | 4 MB/s | 52 MB/s | 248 MB/s |
|
||||
| hash | 4 MB/s | 34 MB/s | 0.9 MB/s | 12 MB/s | 76 MB/s |
|
||||
| secretbox 1K | 1113 op/s | 57583 op/s | 334 op/s | 14227 op/s | 54546 op/s |
|
||||
| box 1K | 145 op/s | 718 op/s | 37 op/s | 368 op/s | 1836 op/s |
|
||||
| scalarMult | 171 op/s | 733 op/s | 56 op/s | 380 op/s | 1882 op/s |
|
||||
| sign | 77 op/s | 200 op/s | 20 op/s | 61 op/s | 592 op/s |
|
||||
| sign.open | 39 op/s | 102 op/s | 11 op/s | 31 op/s | 300 op/s |
|
||||
|
||||
(You can run benchmarks on your devices by clicking on the links at the bottom
|
||||
of the [home page](https://tweetnacl.js.org)).
|
||||
|
||||
In short, with *nacl-fast.js* and 1024-byte messages you can expect to encrypt and
|
||||
authenticate more than 57000 messages per second on a typical laptop or more than
|
||||
14000 messages per second on a $170 smartphone, sign about 500 and verify 300
|
||||
messages per second on a laptop or 60 and 30 messages per second on a smartphone,
|
||||
per CPU core (with Web Workers you can do these operations in parallel),
|
||||
which is good enough for most applications.
|
||||
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
See AUTHORS.md file.
|
||||
|
||||
|
||||
Third-party libraries based on TweetNaCl.js
|
||||
-------------------------------------------
|
||||
|
||||
* [chloride](https://github.com/dominictarr/chloride) - unified API for various NaCl modules
|
||||
* [forward-secrecy](https://github.com/alax/forward-secrecy) — Axolotl ratchet implementation
|
||||
* [nacl-stream](https://github.com/dchest/nacl-stream-js) - streaming encryption
|
||||
* [ristretto255-js](https://github.com/calibra/ristretto255-js) — implementation of the [ristretto255 group](https://ristretto.group/)
|
||||
* [tweetnacl-auth-js](https://github.com/dchest/tweetnacl-auth-js) — implementation of [`crypto_auth`](http://nacl.cr.yp.to/auth.html)
|
||||
* [tweetnacl-js-sealed-box](https://github.com/TogaTech/tweetnacl-js-sealed-box) — fork that adds [`sealed boxes`](https://download.libsodium.org/doc/public-key_cryptography/sealed_boxes.html)
|
||||
* [ed2curve](https://github.com/dchest/ed2curve-js) — convert Ed25519 signing key pair to X25519 boxes key pair
|
||||
|
||||
|
||||
Who uses it
|
||||
-----------
|
||||
|
||||
Some notable users of TweetNaCl.js are listed on the [associated wiki page](https://github.com/dchest/tweetnacl-js/wiki/Who-uses-TweetNaCl.js).
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
// Type definitions for TweetNaCl.js
|
||||
|
||||
export as namespace nacl;
|
||||
|
||||
declare var nacl: nacl;
|
||||
export = nacl;
|
||||
|
||||
declare namespace nacl {
|
||||
export interface BoxKeyPair {
|
||||
publicKey: Uint8Array;
|
||||
secretKey: Uint8Array;
|
||||
}
|
||||
|
||||
export interface SignKeyPair {
|
||||
publicKey: Uint8Array;
|
||||
secretKey: Uint8Array;
|
||||
}
|
||||
|
||||
export interface secretbox {
|
||||
(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
|
||||
open(box: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array | null;
|
||||
readonly keyLength: number;
|
||||
readonly nonceLength: number;
|
||||
readonly overheadLength: number;
|
||||
}
|
||||
|
||||
export interface scalarMult {
|
||||
(n: Uint8Array, p: Uint8Array): Uint8Array;
|
||||
base(n: Uint8Array): Uint8Array;
|
||||
readonly scalarLength: number;
|
||||
readonly groupElementLength: number;
|
||||
}
|
||||
|
||||
namespace boxProps {
|
||||
export interface open {
|
||||
(msg: Uint8Array, nonce: Uint8Array, publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array | null;
|
||||
after(box: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array | null;
|
||||
}
|
||||
|
||||
export interface keyPair {
|
||||
(): BoxKeyPair;
|
||||
fromSecretKey(secretKey: Uint8Array): BoxKeyPair;
|
||||
}
|
||||
}
|
||||
|
||||
export interface box {
|
||||
(msg: Uint8Array, nonce: Uint8Array, publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
||||
before(publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
||||
after(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
|
||||
open: boxProps.open;
|
||||
keyPair: boxProps.keyPair;
|
||||
readonly publicKeyLength: number;
|
||||
readonly secretKeyLength: number;
|
||||
readonly sharedKeyLength: number;
|
||||
readonly nonceLength: number;
|
||||
readonly overheadLength: number;
|
||||
}
|
||||
|
||||
namespace signProps {
|
||||
export interface detached {
|
||||
(msg: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
||||
verify(msg: Uint8Array, sig: Uint8Array, publicKey: Uint8Array): boolean;
|
||||
}
|
||||
|
||||
export interface keyPair {
|
||||
(): SignKeyPair;
|
||||
fromSecretKey(secretKey: Uint8Array): SignKeyPair;
|
||||
fromSeed(secretKey: Uint8Array): SignKeyPair;
|
||||
}
|
||||
}
|
||||
|
||||
export interface sign {
|
||||
(msg: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
||||
open(signedMsg: Uint8Array, publicKey: Uint8Array): Uint8Array | null;
|
||||
detached: signProps.detached;
|
||||
keyPair: signProps.keyPair;
|
||||
readonly publicKeyLength: number;
|
||||
readonly secretKeyLength: number;
|
||||
readonly seedLength: number;
|
||||
readonly signatureLength: number;
|
||||
}
|
||||
|
||||
export interface hash {
|
||||
(msg: Uint8Array): Uint8Array;
|
||||
readonly hashLength: number;
|
||||
}
|
||||
}
|
||||
|
||||
declare interface nacl {
|
||||
randomBytes(n: number): Uint8Array;
|
||||
secretbox: nacl.secretbox;
|
||||
scalarMult: nacl.scalarMult;
|
||||
box: nacl.box;
|
||||
sign: nacl.sign;
|
||||
hash: nacl.hash;
|
||||
verify(x: Uint8Array, y: Uint8Array): boolean;
|
||||
setPRNG(fn: (x: Uint8Array, n: number) => void): void;
|
||||
}
|
||||
Vendored
+1178
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
{type, external}.
|
||||
{realm, local}.
|
||||
{name, tweetnacl}.
|
||||
{version, "1.0.3"}.
|
||||
{deps, []}.
|
||||
@@ -0,0 +1,16 @@
|
||||
{"compilerOptions" : {"target" : "es2022",
|
||||
"strict" : true,
|
||||
"esModuleInterop" : true,
|
||||
"skipLibCheck" : true,
|
||||
"forceConsistentCasingInFileNames" : true,
|
||||
"noImplicitAny" : true,
|
||||
"strictNullChecks" : true,
|
||||
"strictPropertyInitialization" : true,
|
||||
"sourceMap" : true,
|
||||
"outDir" : "dist",
|
||||
"declaration" : true},
|
||||
"$schema" : "https://json.schemastore.org/tsconfig",
|
||||
"display" : "Recommended",
|
||||
"include" : ["src/**/*"],
|
||||
"exclude" : ["src/jx_include"],
|
||||
"composite" : true}
|
||||
+47
-254
@@ -1,267 +1,60 @@
|
||||
=====================================================================
|
||||
tl;dr
|
||||
=====================================================================
|
||||
INTRODUCTION
|
||||
Jex is a simple packaging/dependency system for TypeScript/JavaScript
|
||||
projects.
|
||||
|
||||
You can get this by running jex --help
|
||||
As of now, Jex is a glorified shell script that automates a lot of the
|
||||
tedium in building sidekick, JR, etc. Jex is very much a work in progress,
|
||||
so these instructions are subject to change.
|
||||
|
||||
COMMANDS:
|
||||
man show the manual
|
||||
dwim- init, pull, build
|
||||
dwim+ init, pull, build, mindist, push
|
||||
cfgbarf barf out the jex.eterms file (mostly to make sure it parses correctly)
|
||||
echo home echo $HOME
|
||||
echo jexdir echo $HOME/.jex
|
||||
echo devdir echo $HOME/.jex/dev
|
||||
echo pkgname name of current package
|
||||
echo pkgdir echo $HOME/.jex/dev/realm-name-X.Y.Z
|
||||
echo deps list dependencies of current package
|
||||
echo pathof PKG list the path to PKG or
|
||||
init mkdir -p $HOME/.jex/dev
|
||||
build tsc && cp -r ./src/jex_include ./dist/
|
||||
-w, --weak continue building even if tsc fails
|
||||
-f, --force use cp -rf instead of cp -r
|
||||
mindist mkdir jex_mindist && cp -r src jex_mindist && cp -r dist jex_mindist && rm -r jex_mindist/src/jex_include
|
||||
-f, --force use cp -rf instead of cp -r
|
||||
push rsync -a jex_mindist/ PKGDIR
|
||||
ls ls $HOME/.jex/dev
|
||||
tree tree $HOME/.jex/
|
||||
rmpkg PKG rm -r $HOME/.jex/dev/PKG
|
||||
pull pull each dependency into src/jx_include
|
||||
Jex is currently hyper-specialized to our use cases inside of Vanillae, and
|
||||
is probably (currently) unsuitable for your use case. For instance, Jex
|
||||
assumes you are running a UNIX-like system with standard UNIX programs
|
||||
installed (e.g. tree, rsync, tar, etc). It also makes very strong
|
||||
assumptions about your project structure and how you want to distribute
|
||||
your project.
|
||||
|
||||
Our long-term goal is to build Jex out into a proper secure packaging
|
||||
system, and completely remove any dependency on NPM. NPM comes with a lot
|
||||
of unfixable security issues that present an unacceptable risk in a
|
||||
business context. Developing software for the business context is the
|
||||
focus of the Vanillae project.
|
||||
|
||||
=====================================================================
|
||||
JEX MANUAL
|
||||
=====================================================================
|
||||
jex.eterms CONFIG FILE
|
||||
Every project requires a file called `jex.eterms` in the root of the
|
||||
project.
|
||||
|
||||
Jex is a simple packaging/dependency system for TypeScript/JavaScript
|
||||
projects.
|
||||
The config file is a list of Erlang terms separated by `.`s
|
||||
|
||||
As of now, Jex is a glorified shell script that automates a lot of
|
||||
the tedium in building sidekick, JR, etc. Jex is very much a work in
|
||||
progress, so these instructions are subject to change.
|
||||
The following keys must exist, the rest are ignored
|
||||
|
||||
Jex is currently hyper-specialized to our use cases inside of
|
||||
Vanillae, and is probably (currently) unsuitable for your use case.
|
||||
For instance, Jex assumes you are running a UNIX-like system with
|
||||
standard UNIX programs installed (e.g. tree, rsync, tar, etc). It
|
||||
also makes very strong assumptions about your project structure and
|
||||
how you want to distribute your project.
|
||||
{type, library | external | extension}.
|
||||
This key implies other settings in the build process
|
||||
|
||||
Our long-term goal is to build Jex out into a proper secure packaging
|
||||
system, and completely remove any dependency on NPM. NPM comes with
|
||||
a lot of unfixable security issues that present an unacceptable risk
|
||||
in a business context. Developing software for the business context
|
||||
is the focus of the Vanillae project.
|
||||
library ->
|
||||
The standard option. Pulls in dependencies and builds them,
|
||||
assuming normal project layout. This is what you want in most
|
||||
cases, even for things that are not libraries.
|
||||
|
||||
You'll see how it works and what the philosophy is if you keep
|
||||
reading.
|
||||
external ->
|
||||
This is for external pre-built packages that we didn't develop,
|
||||
such as TweetNaCl. This does not run `tsc`, etc. Jex assumes
|
||||
the project has already been built, and so "install" etc just
|
||||
copies stuff.
|
||||
|
||||
extension ->
|
||||
Used for Jack Russell. Extensions have a much more complicated
|
||||
build process because there are three different execution
|
||||
contexts with three different constraint sets.
|
||||
|
||||
=====================================================================
|
||||
HOW IT WORKS
|
||||
=====================================================================
|
||||
{realm, atom()}.
|
||||
{name, atom()}.
|
||||
{version, string()}.
|
||||
{deps, [string()]}.
|
||||
|
||||
To start off, we run `jex init`
|
||||
PROJECT STRUCTURE
|
||||
Jex makes very strong assumptions about your project structure
|
||||
|
||||
[~] % jex init
|
||||
$ mkdir -p /home/pharpend/.jex/dev
|
||||
|
||||
As I said above, jex is currently a glorified shell script. Much like
|
||||
`make`, jex prints the command it's running with a `$` at the
|
||||
beginning of the line.
|
||||
|
||||
We can get a sense of what will go in this directory by running `jex
|
||||
tree` (yours will not look like this):
|
||||
|
||||
[~] % jex tree
|
||||
$ tree /home/pharpend/.jex
|
||||
/home/pharpend/.jex
|
||||
└── dev
|
||||
├── local-awcp-0.1.0
|
||||
│ ├── dist
|
||||
│ │ ├── awcp.d.ts
|
||||
│ │ ├── awcp.js
|
||||
│ │ ├── awcp.js.map
|
||||
│ │ └── jex_include
|
||||
│ └── src
|
||||
│ └── awcp.ts
|
||||
├── local-parasite-0.1.0
|
||||
│ ├── dist
|
||||
│ │ ├── ae_compiler.d.ts
|
||||
│ │ ├── ae_compiler.js
|
||||
│ │ ├── ae_compiler.js.map
|
||||
│ │ ├── ae_node.d.ts
|
||||
│ │ ├── ae_node.js
|
||||
│ │ ├── ae_node.js.map
|
||||
│ │ ├── jex_include
|
||||
│ │ ├── net.d.ts
|
||||
│ │ ├── net.js
|
||||
│ │ └── net.js.map
|
||||
│ └── src
|
||||
│ ├── ae_compiler.ts
|
||||
│ ├── ae_node.ts
|
||||
│ └── net.ts
|
||||
└── local-sidekick-0.1.0
|
||||
├── dist
|
||||
│ ├── jex_include
|
||||
│ │ └── local-awcp-0.1.0
|
||||
│ │ ├── dist
|
||||
│ │ │ ├── awcp.d.ts
|
||||
│ │ │ ├── awcp.js
|
||||
│ │ │ ├── awcp.js.map
|
||||
│ │ │ └── jex_include
|
||||
│ │ └── src
|
||||
│ │ └── awcp.ts
|
||||
│ ├── sidekick.d.ts
|
||||
│ ├── sidekick.js
|
||||
│ └── sidekick.js.map
|
||||
└── src
|
||||
└── sidekick.ts
|
||||
|
||||
17 directories, 24 files
|
||||
|
||||
Currently, Jex is managing 3 packages for me:
|
||||
|
||||
1. awcp
|
||||
2. parasite
|
||||
3. sidekick
|
||||
|
||||
In a secure context, we want to avoid opaque rewrites whenever
|
||||
possible. This rules out bundling or minifying tools such as
|
||||
browserify. We want the code that is running in the user's browser
|
||||
to be human-readable and to have a straightforward mapping to
|
||||
the original source.
|
||||
|
||||
Let's start with the simplest package which is `awcp`. This is the
|
||||
source directory listing
|
||||
|
||||
[v/libs pharpend/develop] % tree awcp
|
||||
awcp
|
||||
├── dist
|
||||
│ ├── awcp.d.ts
|
||||
│ ├── awcp.js
|
||||
│ ├── awcp.js.map
|
||||
│ └── jex_include
|
||||
├── erl_crash.dump
|
||||
├── jex.eterms
|
||||
├── jex_mindist
|
||||
│ ├── dist
|
||||
│ │ ├── awcp.d.ts
|
||||
│ │ ├── awcp.js
|
||||
│ │ ├── awcp.js.map
|
||||
│ │ └── jex_include
|
||||
│ └── src
|
||||
│ └── awcp.ts
|
||||
├── LICENSE
|
||||
├── Makefile
|
||||
├── README.md
|
||||
├── src
|
||||
│ ├── awcp.ts
|
||||
│ └── jex_include
|
||||
└── tsconfig.json
|
||||
|
||||
8 directories, 14 files
|
||||
|
||||
There is only one source file: `/src/awcp.ts`. There is a directory
|
||||
called `/src/jex_include/` which is empty. If awcp had dependencies,
|
||||
this is where they would go.
|
||||
|
||||
The file tree that ends up in `~/.jex/dev` is the `jex_mindist`
|
||||
directory. Let's focus on that
|
||||
|
||||
[v/libs pharpend/develop] % tree awcp/jex_mindist
|
||||
awcp/jex_mindist
|
||||
├── dist
|
||||
│ ├── awcp.d.ts
|
||||
│ ├── awcp.js
|
||||
│ ├── awcp.js.map
|
||||
│ └── jex_include
|
||||
└── src
|
||||
└── awcp.ts
|
||||
|
||||
3 directories, 4 files
|
||||
|
||||
As you can see, it's the same tree
|
||||
|
||||
[v/libs pharpend/develop] % tree ~/.jex/dev/local-awcp-0.1.0
|
||||
/home/pharpend/.jex/dev/local-awcp-0.1.0
|
||||
├── dist
|
||||
│ ├── awcp.d.ts
|
||||
│ ├── awcp.js
|
||||
│ ├── awcp.js.map
|
||||
│ └── jex_include
|
||||
└── src
|
||||
└── awcp.ts
|
||||
|
||||
3 directories, 4 files
|
||||
|
||||
Briefly, jex is built around the assumptions that you want simplicity,
|
||||
transparency, and composability, possibly at the expense of some
|
||||
duplication.
|
||||
|
||||
It's assumed that you are developing JS to execute in the context of
|
||||
a website, that you are writing only a small amount of JS (i.e. NOT
|
||||
framework JS or a single-page-application), and that serving a
|
||||
JavaScript file tree does not present a bandwidth issue. Everything
|
||||
has a version number, so that you can properly take advantage of
|
||||
caching.
|
||||
|
||||
The idea is that you want to be able to take that file tree above,
|
||||
make it into a tarball, drop it on your server, and have it "just
|
||||
work". Or, you can drop the whole tree into your existing project
|
||||
and have it "just work". We want the source map to work properly, so
|
||||
the TypeScript source is included in the bundle.
|
||||
|
||||
For more context, let's switch over to the Sidekick project. Sidekick
|
||||
depends on AWCP.
|
||||
|
||||
[src/v pharpend/develop] % tree sidekick/src
|
||||
sidekick/src
|
||||
├── jex_include
|
||||
│ └── local-awcp-0.1.0
|
||||
│ ├── dist
|
||||
│ │ ├── awcp.d.ts
|
||||
│ │ ├── awcp.js
|
||||
│ │ ├── awcp.js.map
|
||||
│ │ └── jex_include
|
||||
│ └── src
|
||||
│ └── awcp.ts
|
||||
└── sidekick.ts
|
||||
|
||||
5 directories, 5 files
|
||||
|
||||
Jex automates the process of pulling awcp from the local repository
|
||||
and including it in a predictable file path in the source tree.
|
||||
|
||||
To import AWCP, `sidekick.ts` contains this line
|
||||
|
||||
import * as awcp from './jex_include/local-awcp-0.1.0/dist/awcp.js';
|
||||
|
||||
Notice that we're importing the JS file, not the TS file. TypeScript
|
||||
gets its type information from the `dist/awcp.d.ts` file, not from
|
||||
the `src/awcp.ts` file. The `src/awcp.ts` file is only included so
|
||||
the source map works properly in the browser's debugger.
|
||||
|
||||
The next step is to compile sidekick. To do this, we run `jex dwim-`.
|
||||
This is shorthand for `jex init && jex pull && jex build`
|
||||
|
||||
dwim(minus) ->
|
||||
% make the ~/.jex/dev directory
|
||||
init(),
|
||||
% pull the dependencies into src/jex_include
|
||||
pull(),
|
||||
% run tsc
|
||||
build([]);
|
||||
dwim(plus) ->
|
||||
dwim(minus),
|
||||
% make the jex_mindist folder
|
||||
mindist([]),
|
||||
% push to local repo
|
||||
push().
|
||||
|
||||
This is the essential flow of using Jex. Suppose we update AWCP and
|
||||
want to see that result reflected in sidekick.
|
||||
|
||||
1. In the AWCP repository, we run `jex dwim+`. This makes a new
|
||||
distribution tarball and pushes it to the local repository.
|
||||
2. In the sidekick repository, we run `jex dwim-`. This pulls the new
|
||||
README.md used by documentation tool `typedoc`
|
||||
jex.eterms explained above
|
||||
tsconfig.json needed for tsc
|
||||
src/ typescript source code
|
||||
|
||||
+41
-3
@@ -40,6 +40,10 @@ help() ->
|
||||
% TODONE: jex get_mindist PKG
|
||||
% TODONE: jex dwim++ = install
|
||||
|
||||
% TODO: jex directory based on version
|
||||
% TODO: "external" install
|
||||
% - pull only on library
|
||||
|
||||
% TODO: use less than full qualified names (not priority)
|
||||
% TODO: --name option for docs (not priority)
|
||||
% TODO: make fulldist for arbitrary installed package (requires storing jex.eterms, not hard but also not a priority)
|
||||
@@ -183,11 +187,45 @@ dwim(plus_plus) ->
|
||||
%%-----------------------------------------------------------------------------
|
||||
|
||||
cfgbarf() ->
|
||||
tell(info, "~tp~n", [file:consult("jex.eterms")]).
|
||||
|
||||
tell(info, "~tp~n", [cfg(unsafe)]).
|
||||
|
||||
cfg() ->
|
||||
file:consult("jex.eterms").
|
||||
cfg(safe).
|
||||
|
||||
|
||||
|
||||
-spec cfg(MaybeSafe) -> Result
|
||||
when MaybeSafe :: safe | unsafe
|
||||
Result :: {ok, Cfg :: proplist()}
|
||||
| {error, Reason :: term()}.
|
||||
% @doc "safe" means it checks to make sure the
|
||||
|
||||
cfg(unsafe) ->
|
||||
{ok, Terms} = file:consult("jex.eterms");
|
||||
Terms;
|
||||
cfg(safe) ->
|
||||
case file:consult("jex.eterms") of
|
||||
{ok, Terms} -> cfg2(Terms);
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
cfg2(Cfg) ->
|
||||
% allowed values:
|
||||
% - library
|
||||
% - external
|
||||
% - extension
|
||||
case proplists:get_value(type, Cfg) of
|
||||
% allowed values
|
||||
library -> {ok, Cfg};
|
||||
external -> {ok, Cfg};
|
||||
extension -> {ok, Cfg};
|
||||
% no key
|
||||
undefined -> {error, {no_key, type}};
|
||||
% bad value
|
||||
BadType -> {error, {bad_type, BadType}}
|
||||
end.
|
||||
|
||||
|
||||
|
||||
%%-----------------------------------------------------------------------------
|
||||
%% jex echo
|
||||
|
||||
Reference in New Issue
Block a user