[wip] packaging nacl

This commit is contained in:
Foo Bar
2022-11-27 20:32:22 -07:00
parent 3ddca3db2f
commit 36cb75c51d
9 changed files with 1874 additions and 257 deletions
+5
View File
@@ -0,0 +1,5 @@
{type, site}.
{realm, local}.
{name, jrw}.
{version, "0.1.0"}.
{deps, ["local-awcp-0.2.0"]}.
+2
View File
@@ -0,0 +1,2 @@
docs
erl_crash.dump
+482
View File
@@ -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.
[![Build Status](https://travis-ci.org/dchest/tweetnacl-js.svg?branch=master)
](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).
+98
View File
@@ -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;
}
+1178
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
{type, external}.
{realm, local}.
{name, tweetnacl}.
{version, "1.0.3"}.
{deps, []}.
+16
View File
@@ -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
View File
@@ -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
View File
@@ -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