functionalscript 0.10.3 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bnf/data/module.f.d.ts +67 -1
- package/bnf/data/module.f.js +27 -0
- package/bnf/module.f.js +8 -0
- package/cas/module.f.d.ts +27 -6
- package/cas/module.f.js +38 -32
- package/ci/module.f.d.ts +4 -3
- package/ci/module.f.js +3 -4
- package/crypto/sha2/module.f.d.ts +10 -0
- package/crypto/sha2/module.f.js +8 -0
- package/crypto/sign/module.f.d.ts +15 -0
- package/crypto/sign/module.f.js +15 -0
- package/dev/index/module.f.d.ts +2 -0
- package/dev/index/module.f.js +2 -0
- package/dev/module.f.d.ts +4 -2
- package/dev/module.f.js +29 -28
- package/dev/version/module.f.d.ts +3 -2
- package/dev/version/module.f.js +10 -10
- package/dev/version/test.f.js +3 -6
- package/html/module.f.d.ts +18 -0
- package/html/module.f.js +12 -0
- package/io/module.f.d.ts +4 -2
- package/io/module.f.js +24 -18
- package/package.json +4 -4
- package/types/effects/mock/module.f.d.ts +5 -4
- package/types/effects/mock/module.f.js +6 -4
- package/types/effects/module.d.ts +2 -2
- package/types/effects/module.f.d.ts +17 -19
- package/types/effects/module.f.js +14 -19
- package/types/effects/module.js +4 -3
- package/types/effects/node/module.f.d.ts +31 -35
- package/types/effects/node/module.f.js +16 -7
- package/types/effects/node/test.f.js +23 -25
- package/types/effects/node/virtual/module.f.d.ts +8 -8
- package/types/effects/node/virtual/module.f.js +13 -2
- package/website/module.f.d.ts +3 -1
- package/website/module.f.js +3 -4
- package/ci/module.d.ts +0 -1
- package/ci/module.js +0 -3
- package/dev/index/module.d.ts +0 -1
- package/dev/index/module.js +0 -3
- package/website/module.d.ts +0 -1
- package/website/module.js +0 -3
package/bnf/data/module.f.d.ts
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides grammar-to-data conversion and LL(1) dispatch/matching types and helpers
|
|
3
|
+
* for FunctionalScript BNF rules.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
1
7
|
import { type CodePoint } from '../../text/utf16/module.f.ts';
|
|
2
8
|
import { type RangeMapArray } from '../../types/range_map/module.f.ts';
|
|
3
9
|
import { type Rule as FRule } from '../module.f.ts';
|
|
10
|
+
/**
|
|
11
|
+
* Encoded terminal range value used by BNF data rules.
|
|
12
|
+
*
|
|
13
|
+
* The same as the functional TerminalRange.
|
|
14
|
+
*/
|
|
4
15
|
export type TerminalRange = number;
|
|
16
|
+
/**
|
|
17
|
+
* Ordered list of grammar rule names.
|
|
18
|
+
*/
|
|
5
19
|
export type Sequence = readonly string[];
|
|
6
20
|
/** A variant of rule names. */
|
|
7
21
|
export type Variant = {
|
|
8
22
|
readonly [k in string]: string;
|
|
9
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* Grammar rule definition.
|
|
26
|
+
*
|
|
27
|
+
* It can be one of:
|
|
28
|
+
* - a tagged variant map,
|
|
29
|
+
* - a sequence of referenced rule names,
|
|
30
|
+
* - an encoded terminal range.
|
|
31
|
+
*/
|
|
10
32
|
export type Rule = Variant | Sequence | TerminalRange;
|
|
11
33
|
/** The full grammar */
|
|
12
34
|
export type RuleSet = Readonly<Record<string, Rule>>;
|
|
@@ -29,11 +51,29 @@ type DispatchMap = {
|
|
|
29
51
|
type EmptyTagMap = {
|
|
30
52
|
readonly [id in string]: EmptyTagEntry;
|
|
31
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Recursive descent matcher for a single named rule.
|
|
56
|
+
*/
|
|
32
57
|
export type DescentMatchRule<T> = (name: string, tag: AstTag, s: readonly CodePointMeta<T>[], idx: number) => DescentMatchResult<T>;
|
|
58
|
+
/**
|
|
59
|
+
* Result tuple of a descent match operation: AST node, success flag, and next index.
|
|
60
|
+
*/
|
|
33
61
|
export type DescentMatchResult<T> = readonly [AstRuleMeta<T>, boolean, number];
|
|
62
|
+
/**
|
|
63
|
+
* Entry-point recursive descent matcher.
|
|
64
|
+
*/
|
|
34
65
|
export type DescentMatch<T> = (name: string, s: readonly CodePointMeta<T>[]) => DescentMatchResult<T>;
|
|
66
|
+
/**
|
|
67
|
+
* Code point value paired with metadata.
|
|
68
|
+
*/
|
|
35
69
|
export type CodePointMeta<T> = readonly [CodePoint, T];
|
|
70
|
+
/**
|
|
71
|
+
* AST sequence for the metadata-aware parser.
|
|
72
|
+
*/
|
|
36
73
|
export type AstSequenceMeta<T> = readonly (AstRuleMeta<T> | CodePointMeta<T>)[];
|
|
74
|
+
/**
|
|
75
|
+
* Metadata-aware AST node.
|
|
76
|
+
*/
|
|
37
77
|
export type AstRuleMeta<T> = {
|
|
38
78
|
readonly tag: AstTag;
|
|
39
79
|
readonly sequence: AstSequenceMeta<T>;
|
|
@@ -55,18 +95,44 @@ type AstRule = {
|
|
|
55
95
|
*/
|
|
56
96
|
export type Remainder = readonly CodePoint[] | null;
|
|
57
97
|
/**
|
|
98
|
+
* Parsing result of {@link parser} and {@link parserRuleSet}.
|
|
99
|
+
*
|
|
58
100
|
* Represents the result of a match operation, including the parsed AST rule and the remainder of the input.
|
|
59
101
|
*/
|
|
60
102
|
export type MatchResult = readonly [AstRule, boolean, Remainder];
|
|
61
103
|
/**
|
|
62
|
-
*
|
|
104
|
+
* LL(1) parser function for matching by rule name.
|
|
63
105
|
*/
|
|
64
106
|
export type Match = (name: string, s: readonly CodePoint[]) => MatchResult;
|
|
107
|
+
/**
|
|
108
|
+
* Internal match function signature used by compiled dispatch rules.
|
|
109
|
+
*/
|
|
65
110
|
export type MatchRule = (dr: DispatchRule, s: readonly CodePoint[]) => MatchResult;
|
|
111
|
+
/**
|
|
112
|
+
* Converts a functional grammar rule into serializable BNF data and returns
|
|
113
|
+
* the generated rule set with the entry rule identifier.
|
|
114
|
+
*/
|
|
66
115
|
export declare const toData: (fr: FRule) => readonly [RuleSet, string];
|
|
116
|
+
/**
|
|
117
|
+
* Builds a dispatch map for a {@link RuleSet} to enable predictive parsing.
|
|
118
|
+
*/
|
|
67
119
|
export declare const dispatchMap: (ruleSet: RuleSet) => DispatchMap;
|
|
120
|
+
/**
|
|
121
|
+
* Creates a map that describes whether each rule can consume empty input and,
|
|
122
|
+
* for tagged variants, which tag represents the empty match.
|
|
123
|
+
*/
|
|
68
124
|
export declare const createEmptyTagMap: (data: readonly [RuleSet, string]) => EmptyTagMap;
|
|
125
|
+
/**
|
|
126
|
+
* Creates a recursive descent parser that preserves metadata for each consumed
|
|
127
|
+
* code point.
|
|
128
|
+
*/
|
|
69
129
|
export declare const descentParser: <T>(fr: FRule) => DescentMatch<T>;
|
|
130
|
+
/**
|
|
131
|
+
* Creates an LL(1) parser from a functional grammar rule.
|
|
132
|
+
*/
|
|
70
133
|
export declare const parser: (fr: FRule) => Match;
|
|
134
|
+
/**
|
|
135
|
+
* Creates an LL(1) parser from an already materialized {@link RuleSet}.
|
|
136
|
+
*/
|
|
71
137
|
export declare const parserRuleSet: (ruleSet: RuleSet) => Match;
|
|
72
138
|
export {};
|
package/bnf/data/module.f.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides grammar-to-data conversion and LL(1) dispatch/matching types and helpers
|
|
3
|
+
* for FunctionalScript BNF rules.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
1
7
|
import { stringToCodePointList } from "../../text/utf16/module.f.js";
|
|
2
8
|
import { strictEqual } from "../../types/function/operator/module.f.js";
|
|
3
9
|
import { map, toArray } from "../../types/list/module.f.js";
|
|
@@ -74,6 +80,10 @@ const toDataAdd = (map) => (fr) => {
|
|
|
74
80
|
const [map2, set, rule] = newRule(map1);
|
|
75
81
|
return [map2, { ...set, [id]: rule }, id];
|
|
76
82
|
};
|
|
83
|
+
/**
|
|
84
|
+
* Converts a functional grammar rule into serializable BNF data and returns
|
|
85
|
+
* the generated rule set with the entry rule identifier.
|
|
86
|
+
*/
|
|
77
87
|
export const toData = (fr) => {
|
|
78
88
|
const [, ruleSet, id] = toDataAdd({})(fr);
|
|
79
89
|
return [ruleSet, id];
|
|
@@ -91,6 +101,9 @@ const dispatchOp = rangeMap({
|
|
|
91
101
|
equal: strictEqual,
|
|
92
102
|
def: null,
|
|
93
103
|
});
|
|
104
|
+
/**
|
|
105
|
+
* Builds a dispatch map for a {@link RuleSet} to enable predictive parsing.
|
|
106
|
+
*/
|
|
94
107
|
export const dispatchMap = (ruleSet) => {
|
|
95
108
|
const addRuleToDispatch = (dr, name) => {
|
|
96
109
|
if (dr === null)
|
|
@@ -196,9 +209,17 @@ const emptyTagMapAdd = (ruleSet) => (map) => (name) => {
|
|
|
196
209
|
return [ruleSet, { ...map, [name]: emptyTag }, emptyTag];
|
|
197
210
|
}
|
|
198
211
|
};
|
|
212
|
+
/**
|
|
213
|
+
* Creates a map that describes whether each rule can consume empty input and,
|
|
214
|
+
* for tagged variants, which tag represents the empty match.
|
|
215
|
+
*/
|
|
199
216
|
export const createEmptyTagMap = (data) => {
|
|
200
217
|
return emptyTagMapAdd(data[0])({})(data[1])[1];
|
|
201
218
|
};
|
|
219
|
+
/**
|
|
220
|
+
* Creates a recursive descent parser that preserves metadata for each consumed
|
|
221
|
+
* code point.
|
|
222
|
+
*/
|
|
202
223
|
export const descentParser = (fr) => {
|
|
203
224
|
const data = toData(fr);
|
|
204
225
|
const emptyTagMap = createEmptyTagMap(data);
|
|
@@ -256,10 +277,16 @@ export const descentParser = (fr) => {
|
|
|
256
277
|
};
|
|
257
278
|
return match;
|
|
258
279
|
};
|
|
280
|
+
/**
|
|
281
|
+
* Creates an LL(1) parser from a functional grammar rule.
|
|
282
|
+
*/
|
|
259
283
|
export const parser = (fr) => {
|
|
260
284
|
const data = toData(fr);
|
|
261
285
|
return parserRuleSet(data[0]);
|
|
262
286
|
};
|
|
287
|
+
/**
|
|
288
|
+
* Creates an LL(1) parser from an already materialized {@link RuleSet}.
|
|
289
|
+
*/
|
|
263
290
|
export const parserRuleSet = (ruleSet) => {
|
|
264
291
|
const map = dispatchMap(ruleSet);
|
|
265
292
|
const f = (rule, cp) => {
|
package/bnf/module.f.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core BNF grammar primitives and helpers for describing parser rules.
|
|
3
|
+
*
|
|
4
|
+
* The module provides terminal-range encoding utilities, rule composition
|
|
5
|
+
* types, and set/range helpers used by FunctionalScript grammar definitions.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
1
9
|
import { codePointListToString, stringToCodePointList } from "../text/utf16/module.f.js";
|
|
2
10
|
import { isArray2 } from "../types/array/module.f.js";
|
|
3
11
|
import { map, toArray, repeat as listRepeat } from "../types/list/module.f.js";
|
package/cas/module.f.d.ts
CHANGED
|
@@ -5,19 +5,40 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { type Sha2 } from "../crypto/sha2/module.f.ts";
|
|
7
7
|
import type { Vec } from "../types/bit_vec/module.f.ts";
|
|
8
|
-
import { type Effect, type
|
|
9
|
-
import { type Fs, type
|
|
10
|
-
export type KvStore<O extends
|
|
8
|
+
import { type Effect, type Operation } from "../types/effects/module.f.ts";
|
|
9
|
+
import { type Fs, type NodeOp } from "../types/effects/node/module.f.ts";
|
|
10
|
+
export type KvStore<O extends Operation> = {
|
|
11
|
+
/** Reads a value by key; returns `undefined` when the key does not exist. */
|
|
11
12
|
readonly read: (key: Vec) => Effect<O, Vec | undefined>;
|
|
13
|
+
/** Writes a key/value pair to the underlying storage. */
|
|
12
14
|
readonly write: (key: Vec, value: Vec) => Effect<O, void>;
|
|
15
|
+
/** Lists all keys available in the store. */
|
|
13
16
|
readonly list: () => Effect<O, readonly Vec[]>;
|
|
14
17
|
};
|
|
18
|
+
/** A key/value tuple where index `0` is the key and index `1` is the value. */
|
|
15
19
|
export type Kv = readonly [Vec, Vec];
|
|
20
|
+
/**
|
|
21
|
+
* Creates a filesystem-backed key/value store under the provided root path.
|
|
22
|
+
*/
|
|
16
23
|
export declare const fileKvStore: (path: string) => KvStore<Fs>;
|
|
17
|
-
export type Cas<O extends
|
|
24
|
+
export type Cas<O extends Operation> = {
|
|
25
|
+
/** Reads content by hash; returns `undefined` when not found. */
|
|
18
26
|
readonly read: (key: Vec) => Effect<O, Vec | undefined>;
|
|
27
|
+
/** Stores content and returns its computed hash. */
|
|
19
28
|
readonly write: (value: Vec) => Effect<O, Vec>;
|
|
29
|
+
/** Lists all stored content hashes. */
|
|
20
30
|
readonly list: () => Effect<O, readonly Vec[]>;
|
|
21
31
|
};
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Builds a content-addressable storage facade from a SHA-2 implementation.
|
|
34
|
+
*/
|
|
35
|
+
export declare const cas: (sha2: Sha2) => <O extends Operation>(_: KvStore<O>) => Cas<O>;
|
|
36
|
+
/**
|
|
37
|
+
* Runs the CAS CLI.
|
|
38
|
+
*
|
|
39
|
+
* Supported subcommands:
|
|
40
|
+
* - `add <path>`: stores file content and prints the hash.
|
|
41
|
+
* - `get <hash> <path>`: restores content by hash into a file.
|
|
42
|
+
* - `list`: prints all known hashes.
|
|
43
|
+
*/
|
|
44
|
+
export declare const main: (args: readonly string[]) => Effect<NodeOp, number>;
|
package/cas/module.f.js
CHANGED
|
@@ -6,62 +6,73 @@
|
|
|
6
6
|
import { computeSync, sha256 } from "../crypto/sha2/module.f.js";
|
|
7
7
|
import { parse } from "../path/module.f.js";
|
|
8
8
|
import { cBase32ToVec, vecToCBase32 } from "../types/cbase32/module.f.js";
|
|
9
|
-
import {
|
|
9
|
+
import { begin, pure } from "../types/effects/module.f.js";
|
|
10
10
|
import { error, log, mkdir, readdir, readFile, writeFile } from "../types/effects/node/module.f.js";
|
|
11
11
|
import { toOption } from "../types/nullable/module.f.js";
|
|
12
12
|
import { unwrap } from "../types/result/module.f.js";
|
|
13
13
|
const o = { withFileTypes: true };
|
|
14
14
|
const split = (s) => [s.substring(0, 2), s.substring(2)];
|
|
15
15
|
const prefix = '.cas';
|
|
16
|
+
/** Converts a content key to its sharded relative CAS file path. */
|
|
16
17
|
const toPath = (key) => {
|
|
17
18
|
const s = vecToCBase32(key);
|
|
18
19
|
const [a, bc] = split(s);
|
|
19
20
|
const [b, c] = split(bc);
|
|
20
21
|
return `${prefix}/${a}/${b}/${c}`;
|
|
21
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* Creates a filesystem-backed key/value store under the provided root path.
|
|
25
|
+
*/
|
|
22
26
|
export const fileKvStore = (path) => ({
|
|
23
|
-
read: (key) =>
|
|
27
|
+
read: (key) => begin
|
|
24
28
|
.step(() => readFile(toPath(key)))
|
|
25
|
-
.step(([status, data]) => pure(status === 'error' ? undefined : data))
|
|
26
|
-
.effect,
|
|
29
|
+
.step(([status, data]) => pure(status === 'error' ? undefined : data)),
|
|
27
30
|
write: (key, value) => {
|
|
28
31
|
const p = toPath(key);
|
|
29
32
|
const parts = parse(p);
|
|
30
33
|
const dir = `${path}/${parts.slice(0, -1).join('/')}`;
|
|
31
34
|
// TODO: error handling
|
|
32
|
-
return
|
|
35
|
+
return begin
|
|
33
36
|
.step(() => mkdir(dir, { recursive: true }))
|
|
34
37
|
.step(() => writeFile(`${path}/${p}`, value))
|
|
35
|
-
.step(() => pure(undefined))
|
|
36
|
-
.effect;
|
|
38
|
+
.step(() => pure(undefined));
|
|
37
39
|
},
|
|
38
40
|
list: () =>
|
|
39
41
|
// TODO: remove unwrap
|
|
40
|
-
|
|
42
|
+
begin
|
|
41
43
|
.step(() => readdir('.cas', { recursive: true }))
|
|
42
44
|
.step(r => pure(unwrap(r).flatMap(({ name, parentPath, isFile }) => toOption(isFile
|
|
43
45
|
? cBase32ToVec(parentPath.substring(prefix.length).replaceAll('/', '') + name)
|
|
44
|
-
: null))))
|
|
45
|
-
.effect,
|
|
46
|
+
: null)))),
|
|
46
47
|
});
|
|
48
|
+
/**
|
|
49
|
+
* Builds a content-addressable storage facade from a SHA-2 implementation.
|
|
50
|
+
*/
|
|
47
51
|
export const cas = (sha2) => {
|
|
48
52
|
const compute = computeSync(sha2);
|
|
49
53
|
return ({ read, write, list }) => ({
|
|
50
54
|
read,
|
|
51
55
|
write: (value) => {
|
|
52
56
|
const hash = compute([value]);
|
|
53
|
-
return
|
|
57
|
+
return begin
|
|
54
58
|
.step(() => write(hash, value))
|
|
55
|
-
.step(() => pure(hash))
|
|
56
|
-
.effect;
|
|
59
|
+
.step(() => pure(hash));
|
|
57
60
|
},
|
|
58
61
|
list,
|
|
59
62
|
});
|
|
60
63
|
};
|
|
61
|
-
|
|
64
|
+
/** Prints an error message and returns exit code `1`. */
|
|
65
|
+
const e = (s) => begin
|
|
62
66
|
.step(() => error(s))
|
|
63
|
-
.step(() => pure(1))
|
|
64
|
-
|
|
67
|
+
.step(() => pure(1));
|
|
68
|
+
/**
|
|
69
|
+
* Runs the CAS CLI.
|
|
70
|
+
*
|
|
71
|
+
* Supported subcommands:
|
|
72
|
+
* - `add <path>`: stores file content and prints the hash.
|
|
73
|
+
* - `get <hash> <path>`: restores content by hash into a file.
|
|
74
|
+
* - `list`: prints all known hashes.
|
|
75
|
+
*/
|
|
65
76
|
export const main = (args) => {
|
|
66
77
|
const c = cas(sha256)(fileKvStore('.'));
|
|
67
78
|
const [cmd, ...options] = args;
|
|
@@ -71,12 +82,11 @@ export const main = (args) => {
|
|
|
71
82
|
return e("'cas add' expects one parameter");
|
|
72
83
|
}
|
|
73
84
|
const [path] = options;
|
|
74
|
-
return
|
|
85
|
+
return begin
|
|
75
86
|
.step(() => readFile(path))
|
|
76
87
|
.step(v => c.write(unwrap(v)))
|
|
77
88
|
.step(hash => log(vecToCBase32(hash)))
|
|
78
|
-
.step(() => pure(0))
|
|
79
|
-
.effect;
|
|
89
|
+
.step(() => pure(0));
|
|
80
90
|
}
|
|
81
91
|
case 'get': {
|
|
82
92
|
if (options.length !== 2) {
|
|
@@ -87,36 +97,32 @@ export const main = (args) => {
|
|
|
87
97
|
if (hash === null) {
|
|
88
98
|
return e(`invalid hash format: ${hashCBase32}`);
|
|
89
99
|
}
|
|
90
|
-
return
|
|
100
|
+
return begin
|
|
91
101
|
.step(() => c.read(hash))
|
|
92
102
|
.step(v => {
|
|
93
103
|
const result = v === undefined
|
|
94
104
|
? e(`no such hash: ${hashCBase32}`)
|
|
95
|
-
:
|
|
105
|
+
: begin
|
|
96
106
|
.step(() => writeFile(path, v))
|
|
97
|
-
.step(() => pure(0))
|
|
98
|
-
.effect;
|
|
107
|
+
.step(() => pure(0));
|
|
99
108
|
return result;
|
|
100
|
-
})
|
|
101
|
-
.effect;
|
|
109
|
+
});
|
|
102
110
|
}
|
|
103
111
|
case 'list': {
|
|
104
|
-
return
|
|
112
|
+
return begin
|
|
105
113
|
.step(() => c.list())
|
|
106
114
|
.step(v => {
|
|
107
115
|
// TODO: make it lazy.
|
|
108
|
-
let i =
|
|
116
|
+
let i = begin;
|
|
109
117
|
for (const j of v) {
|
|
110
118
|
const prev = i;
|
|
111
|
-
i =
|
|
119
|
+
i = begin
|
|
112
120
|
.step(() => prev)
|
|
113
|
-
.step(() => log(vecToCBase32(j)))
|
|
114
|
-
.effect;
|
|
121
|
+
.step(() => log(vecToCBase32(j)));
|
|
115
122
|
}
|
|
116
123
|
return i;
|
|
117
124
|
})
|
|
118
|
-
.step(() => pure(0))
|
|
119
|
-
.effect;
|
|
125
|
+
.step(() => pure(0));
|
|
120
126
|
}
|
|
121
127
|
case undefined: {
|
|
122
128
|
return e('Error: CAS command requires subcommand');
|
package/ci/module.f.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
|
|
3
|
-
declare const
|
|
1
|
+
import { type Effect } from '../types/effects/module.f.ts';
|
|
2
|
+
import { type NodeOp } from '../types/effects/node/module.f.ts';
|
|
3
|
+
export declare const effect: Effect<NodeOp, number>;
|
|
4
|
+
declare const _default: () => Effect<NodeOp, number>;
|
|
4
5
|
export default _default;
|
package/ci/module.f.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import { utf8 } from "../text/module.f.js";
|
|
7
|
-
import {
|
|
7
|
+
import { begin, pure } from "../types/effects/module.f.js";
|
|
8
8
|
import { writeFile } from "../types/effects/node/module.f.js";
|
|
9
9
|
const os = ['ubuntu', 'macos', 'windows'];
|
|
10
10
|
const architecture = ['intel', 'arm'];
|
|
@@ -177,8 +177,7 @@ const gha = {
|
|
|
177
177
|
on: { pull_request: {} },
|
|
178
178
|
jobs,
|
|
179
179
|
};
|
|
180
|
-
export const effect =
|
|
180
|
+
export const effect = begin
|
|
181
181
|
.step(() => writeFile('.github/workflows/ci.yml', utf8(JSON.stringify(gha, null, ' '))))
|
|
182
|
-
.step(() => pure(0))
|
|
183
|
-
.effect;
|
|
182
|
+
.step(() => pure(0));
|
|
184
183
|
export default () => effect;
|
|
@@ -7,7 +7,9 @@ import type { Array16, Array8 } from '../../types/array/module.f.ts';
|
|
|
7
7
|
import { type Vec } from '../../types/bit_vec/module.f.ts';
|
|
8
8
|
import type { Fold } from '../../types/function/operator/module.f.ts';
|
|
9
9
|
import { type List } from '../../types/list/module.f.ts';
|
|
10
|
+
/** 8-word SHA-2 state vector. */
|
|
10
11
|
export type V8 = Array8<bigint>;
|
|
12
|
+
/** 16-word SHA-2 message schedule chunk. */
|
|
11
13
|
export type V16 = Array16<bigint>;
|
|
12
14
|
/**
|
|
13
15
|
* Type definition for the state of the SHA-2 algorithm.
|
|
@@ -75,8 +77,16 @@ export type Sha2 = {
|
|
|
75
77
|
*/
|
|
76
78
|
readonly end: (state: State) => Vec;
|
|
77
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Computes a SHA-2 hash from a list of message chunks.
|
|
82
|
+
*
|
|
83
|
+
* @param sha2 A SHA-2 algorithm configuration.
|
|
84
|
+
* @returns A function that hashes the full list of chunks.
|
|
85
|
+
*/
|
|
78
86
|
export declare const computeSync: ({ append, init, end }: Sha2) => (list: List<Vec>) => Vec;
|
|
87
|
+
/** 32-bit SHA-2 base configuration shared by SHA-224 and SHA-256. */
|
|
79
88
|
export declare const base32: Base;
|
|
89
|
+
/** 64-bit SHA-2 base configuration shared by SHA-384, SHA-512, SHA-512/224 and SHA-512/256. */
|
|
80
90
|
export declare const base64: Base;
|
|
81
91
|
/** SHA-256 */
|
|
82
92
|
export declare const sha256: Sha2;
|
package/crypto/sha2/module.f.js
CHANGED
|
@@ -167,10 +167,17 @@ const sha2 = ({ append, end, chunkLength }, hash, hashLength) => ({
|
|
|
167
167
|
append,
|
|
168
168
|
end: end(hashLength),
|
|
169
169
|
});
|
|
170
|
+
/**
|
|
171
|
+
* Computes a SHA-2 hash from a list of message chunks.
|
|
172
|
+
*
|
|
173
|
+
* @param sha2 A SHA-2 algorithm configuration.
|
|
174
|
+
* @returns A function that hashes the full list of chunks.
|
|
175
|
+
*/
|
|
170
176
|
export const computeSync = ({ append, init, end }) => {
|
|
171
177
|
const f = fold(append)(init);
|
|
172
178
|
return (list) => end(f(list));
|
|
173
179
|
};
|
|
180
|
+
/** 32-bit SHA-2 base configuration shared by SHA-224 and SHA-256. */
|
|
174
181
|
export const base32 = base({
|
|
175
182
|
logBitLen: 5n,
|
|
176
183
|
k: [
|
|
@@ -196,6 +203,7 @@ export const base32 = base({
|
|
|
196
203
|
ss0: [7n, 18n, 3n],
|
|
197
204
|
ss1: [17n, 19n, 10n],
|
|
198
205
|
});
|
|
206
|
+
/** 64-bit SHA-2 base configuration shared by SHA-384, SHA-512, SHA-512/224 and SHA-512/256. */
|
|
199
207
|
export const base64 = base({
|
|
200
208
|
logBitLen: 6n,
|
|
201
209
|
k: [
|
|
@@ -14,10 +14,25 @@ export type All = {
|
|
|
14
14
|
readonly int2octets: (x: bigint) => Vec;
|
|
15
15
|
readonly bits2octets: (b: Vec) => Vec;
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Builds RFC6979 helper conversions for a subgroup order.
|
|
19
|
+
*
|
|
20
|
+
* @param q - Subgroup order.
|
|
21
|
+
* @returns Conversion helpers used by deterministic nonce generation.
|
|
22
|
+
*/
|
|
17
23
|
export declare const all: (q: bigint) => All;
|
|
24
|
+
/**
|
|
25
|
+
* Builds RFC6979 helper conversions from curve parameters.
|
|
26
|
+
*/
|
|
18
27
|
export declare const fromCurve: (c: Curve) => All;
|
|
19
28
|
export declare const concat: (...x: readonly Vec[]) => Vec;
|
|
29
|
+
/**
|
|
30
|
+
* Computes deterministic ECDSA nonce `k` as described by RFC6979.
|
|
31
|
+
*/
|
|
20
32
|
export declare const computeK: (_: All) => (_: Sha2) => (x: bigint) => (m: Vec) => bigint;
|
|
21
33
|
type Signature = Array2<bigint>;
|
|
34
|
+
/**
|
|
35
|
+
* Signs a message bit vector and returns an ECDSA `(r, s)` signature pair.
|
|
36
|
+
*/
|
|
22
37
|
export declare const sign: (c: Curve) => (hf: Sha2) => (x: bigint) => (m: Vec) => Signature;
|
|
23
38
|
export {};
|
package/crypto/sign/module.f.js
CHANGED
|
@@ -5,6 +5,12 @@ import { computeSync } from "../sha2/module.f.js";
|
|
|
5
5
|
// qlen to rlen
|
|
6
6
|
const roundUp8 = roundUp(8n);
|
|
7
7
|
const divUp8 = divUp(8n);
|
|
8
|
+
/**
|
|
9
|
+
* Builds RFC6979 helper conversions for a subgroup order.
|
|
10
|
+
*
|
|
11
|
+
* @param q - Subgroup order.
|
|
12
|
+
* @returns Conversion helpers used by deterministic nonce generation.
|
|
13
|
+
*/
|
|
8
14
|
export const all = (q) => {
|
|
9
15
|
const qlen = bitLength(q);
|
|
10
16
|
const bits2int = (b) => {
|
|
@@ -22,11 +28,17 @@ export const all = (q) => {
|
|
|
22
28
|
bits2octets: b => int2octets(bits2int(b) % q),
|
|
23
29
|
};
|
|
24
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Builds RFC6979 helper conversions from curve parameters.
|
|
33
|
+
*/
|
|
25
34
|
export const fromCurve = (c) => all(c.nf.p);
|
|
26
35
|
const x01 = vec8(0x01n);
|
|
27
36
|
const x00 = vec8(0x00n);
|
|
28
37
|
const ltov = listToVec(msb);
|
|
29
38
|
export const concat = (...x) => ltov(x);
|
|
39
|
+
/**
|
|
40
|
+
* Computes deterministic ECDSA nonce `k` as described by RFC6979.
|
|
41
|
+
*/
|
|
30
42
|
export const computeK = ({ q, bits2int, qlen, int2octets, bits2octets }) => hf => {
|
|
31
43
|
// TODO: Look at https://www.rfc-editor.org/rfc/rfc6979#section-3.3 to reformulate
|
|
32
44
|
// it using `HMAC_DRBG`.
|
|
@@ -96,6 +108,9 @@ export const computeK = ({ q, bits2int, qlen, int2octets, bits2octets }) => hf =
|
|
|
96
108
|
}
|
|
97
109
|
};
|
|
98
110
|
};
|
|
111
|
+
/**
|
|
112
|
+
* Signs a message bit vector and returns an ECDSA `(r, s)` signature pair.
|
|
113
|
+
*/
|
|
99
114
|
export const sign = (c) => (hf) => (x) => (m) => {
|
|
100
115
|
// 2.4 Signature Generation
|
|
101
116
|
const { nf: { p: q, div }, g } = c;
|
package/dev/module.f.d.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import { type Io } from '../io/module.f.ts';
|
|
7
|
+
import { type All, type NodeProgram, type Readdir } from '../types/effects/node/module.f.ts';
|
|
8
|
+
import { type Effect } from '../types/effects/module.f.ts';
|
|
7
9
|
export declare const todo: () => never;
|
|
8
10
|
export type Module = {
|
|
9
11
|
readonly default?: unknown;
|
|
@@ -12,6 +14,6 @@ export type ModuleMap = {
|
|
|
12
14
|
readonly [k in string]: Module;
|
|
13
15
|
};
|
|
14
16
|
export declare const env: (io: Io) => (v: string) => string | undefined;
|
|
15
|
-
export declare const
|
|
17
|
+
export declare const allFiles2: (s: string) => Effect<Readdir | All, readonly string[]>;
|
|
16
18
|
export declare const loadModuleMap: (io: Io) => Promise<ModuleMap>;
|
|
17
|
-
export declare const
|
|
19
|
+
export declare const index4: NodeProgram;
|
package/dev/module.f.js
CHANGED
|
@@ -5,11 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { fromIo } from "../io/module.f.js";
|
|
7
7
|
import { updateVersion } from "./version/module.f.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { utf8ToString } from "../text/module.f.js";
|
|
8
|
+
import { all, both, readdir, readFile, writeFile } from "../types/effects/node/module.f.js";
|
|
9
|
+
import { utf8, utf8ToString } from "../text/module.f.js";
|
|
11
10
|
import { unwrap } from "../types/result/module.f.js";
|
|
12
|
-
import {
|
|
11
|
+
import { begin, pure } from "../types/effects/module.f.js";
|
|
13
12
|
export const todo = () => { throw 'not implemented'; };
|
|
14
13
|
const cmp = ([a], [b]) => a < b ? -1 : a > b ? 1 : 0;
|
|
15
14
|
export const env = ({ process: { env } }) => a => {
|
|
@@ -18,31 +17,34 @@ export const env = ({ process: { env } }) => a => {
|
|
|
18
17
|
typeof r.get === 'function' ? r.get() :
|
|
19
18
|
r.value;
|
|
20
19
|
};
|
|
21
|
-
export const
|
|
22
|
-
const
|
|
23
|
-
|
|
20
|
+
export const allFiles2 = (s) => {
|
|
21
|
+
const load = (p) => begin
|
|
22
|
+
.step(() => readdir(p, {}))
|
|
23
|
+
.step(d => {
|
|
24
24
|
let result = [];
|
|
25
|
-
for (const i of
|
|
25
|
+
for (const i of unwrap(d)) {
|
|
26
26
|
const { name } = i;
|
|
27
27
|
if (name.startsWith('.')) {
|
|
28
28
|
continue;
|
|
29
29
|
}
|
|
30
30
|
const file = `${p}/${name}`;
|
|
31
|
-
if (i.
|
|
31
|
+
if (!i.isFile) {
|
|
32
32
|
if (name === 'node_modules') {
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
|
-
result = [...result,
|
|
35
|
+
result = [...result, load(file)];
|
|
36
36
|
continue;
|
|
37
37
|
}
|
|
38
38
|
if (name.endsWith('.js') || name.endsWith('.ts')) {
|
|
39
|
-
result = [...result, file];
|
|
39
|
+
result = [...result, pure([file])];
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
return result;
|
|
43
|
-
}
|
|
42
|
+
return all(...result);
|
|
43
|
+
})
|
|
44
|
+
.step(v => pure(v.flat()));
|
|
44
45
|
return load(s);
|
|
45
46
|
};
|
|
47
|
+
const allFiles = (io) => (s) => fromIo(io)(allFiles2(s));
|
|
46
48
|
export const loadModuleMap = async (io) => {
|
|
47
49
|
const { fs: { existsSync }, asyncImport } = io;
|
|
48
50
|
let map = [];
|
|
@@ -58,22 +60,21 @@ export const loadModuleMap = async (io) => {
|
|
|
58
60
|
return Object.fromEntries(map.toSorted(cmp));
|
|
59
61
|
};
|
|
60
62
|
const denoJson = './deno.json';
|
|
61
|
-
const index2 =
|
|
63
|
+
const index2 = begin
|
|
62
64
|
.step(() => updateVersion)
|
|
63
65
|
.step(() => readFile(denoJson))
|
|
64
|
-
.step(v => pure(JSON.parse(utf8ToString(unwrap(v)))))
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
const list = (await allFiles(io)('.')).filter(v => v.endsWith('/module.f.ts') || v.endsWith('/module.ts'));
|
|
70
|
-
// console.log(list)
|
|
66
|
+
.step(v => pure(JSON.parse(utf8ToString(unwrap(v)))));
|
|
67
|
+
const allFiles2aa = begin
|
|
68
|
+
.step(() => allFiles2('.'))
|
|
69
|
+
.step(files => {
|
|
70
|
+
const list = files.filter(v => v.endsWith('/module.f.ts') || v.endsWith('/module.ts'));
|
|
71
71
|
const exportsA = list.map(v => [v, `./${v.substring(2)}`]);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
return pure(Object.fromEntries(exportsA));
|
|
73
|
+
});
|
|
74
|
+
const index3 = both(index2)(allFiles2aa)
|
|
75
|
+
.step(([jsr_json, exports]) => {
|
|
75
76
|
const json = JSON.stringify({ ...jsr_json, exports }, null, 2);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
return writeFile(denoJson, utf8(json));
|
|
78
|
+
})
|
|
79
|
+
.step(() => pure(0));
|
|
80
|
+
export const index4 = () => index3;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
|
|
1
|
+
import { type Effect } from "../../types/effects/module.f.ts";
|
|
2
|
+
import { type All, type ReadFile, type WriteFile } from "../../types/effects/node/module.f.ts";
|
|
3
|
+
export declare const updateVersion: Effect<ReadFile | WriteFile | All, number>;
|