@datumesh/verify 0.0.0-stage → 0.1.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/LICENSE +202 -0
- package/README.md +54 -2
- package/dist/cjs/ed25519.d.ts +2 -0
- package/dist/cjs/ed25519.js +82 -0
- package/dist/cjs/errors.d.ts +14 -0
- package/dist/cjs/errors.js +61 -0
- package/dist/cjs/hash.d.ts +6 -0
- package/dist/cjs/hash.js +27 -0
- package/dist/cjs/index.d.ts +27 -0
- package/dist/cjs/index.js +83 -0
- package/dist/cjs/jcs.d.ts +5 -0
- package/dist/cjs/jcs.js +25 -0
- package/dist/cjs/json.d.ts +29 -0
- package/dist/cjs/json.js +321 -0
- package/dist/cjs/keys.d.ts +73 -0
- package/dist/cjs/keys.js +257 -0
- package/dist/cjs/lexical.d.ts +59 -0
- package/dist/cjs/lexical.js +207 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/unicode-data.d.ts +8 -0
- package/dist/cjs/unicode-data.js +14 -0
- package/dist/cjs/unicode.d.ts +12 -0
- package/dist/cjs/unicode.js +192 -0
- package/dist/cjs/uri.d.ts +31 -0
- package/dist/cjs/uri.js +201 -0
- package/dist/cjs/utf8.d.ts +10 -0
- package/dist/cjs/utf8.js +101 -0
- package/dist/cjs/validate.d.ts +17 -0
- package/dist/cjs/validate.js +349 -0
- package/dist/cjs/verify.d.ts +38 -0
- package/dist/cjs/verify.js +192 -0
- package/dist/esm/ed25519.d.ts +2 -0
- package/dist/esm/ed25519.js +79 -0
- package/dist/esm/errors.d.ts +14 -0
- package/dist/esm/errors.js +56 -0
- package/dist/esm/hash.d.ts +6 -0
- package/dist/esm/hash.js +21 -0
- package/dist/esm/index.d.ts +27 -0
- package/dist/esm/index.js +63 -0
- package/dist/esm/jcs.d.ts +5 -0
- package/dist/esm/jcs.js +18 -0
- package/dist/esm/json.d.ts +29 -0
- package/dist/esm/json.js +309 -0
- package/dist/esm/keys.d.ts +73 -0
- package/dist/esm/keys.js +249 -0
- package/dist/esm/lexical.d.ts +59 -0
- package/dist/esm/lexical.js +187 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/unicode-data.d.ts +8 -0
- package/dist/esm/unicode-data.js +11 -0
- package/dist/esm/unicode.d.ts +12 -0
- package/dist/esm/unicode.js +184 -0
- package/dist/esm/uri.d.ts +31 -0
- package/dist/esm/uri.js +196 -0
- package/dist/esm/utf8.d.ts +10 -0
- package/dist/esm/utf8.js +96 -0
- package/dist/esm/validate.d.ts +17 -0
- package/dist/esm/validate.js +340 -0
- package/dist/esm/verify.d.ts +38 -0
- package/dist/esm/verify.js +189 -0
- package/package.json +54 -4
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isAssigned = isAssigned;
|
|
4
|
+
exports.combiningClass = combiningClass;
|
|
5
|
+
exports.nfc = nfc;
|
|
6
|
+
exports.codePoints = codePoints;
|
|
7
|
+
exports.isNFC = isNFC;
|
|
8
|
+
exports.hasLoneSurrogate = hasLoneSurrogate;
|
|
9
|
+
// Unicode 15.1 character checks for SPEC.md 3.5, from tables shipped with this library
|
|
10
|
+
// (src/unicode-data.ts). Nothing here calls String.prototype.normalize or uses \p{...}
|
|
11
|
+
// regular expressions, so results do not depend on the JavaScript engine's Unicode version.
|
|
12
|
+
const unicode_data_js_1 = require("./unicode-data.js");
|
|
13
|
+
/** True when the code point is assigned in Unicode 15.1 (General_Category != Cn). */
|
|
14
|
+
function isAssigned(cp) {
|
|
15
|
+
let lo = 0;
|
|
16
|
+
let hi = unicode_data_js_1.ASSIGNED.length / 2 - 1;
|
|
17
|
+
while (lo <= hi) {
|
|
18
|
+
const mid = (lo + hi) >> 1;
|
|
19
|
+
if (cp < unicode_data_js_1.ASSIGNED[2 * mid])
|
|
20
|
+
hi = mid - 1;
|
|
21
|
+
else if (cp > unicode_data_js_1.ASSIGNED[2 * mid + 1])
|
|
22
|
+
lo = mid + 1;
|
|
23
|
+
else
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
let cccMap = null;
|
|
29
|
+
let decompMap = null;
|
|
30
|
+
let compMap = null;
|
|
31
|
+
function tables() {
|
|
32
|
+
if (cccMap)
|
|
33
|
+
return;
|
|
34
|
+
const c = new Map();
|
|
35
|
+
for (const run of unicode_data_js_1.CCC.split(';')) {
|
|
36
|
+
const [a, b, k] = run.split(':');
|
|
37
|
+
const end = parseInt(b, 16);
|
|
38
|
+
for (let cp = parseInt(a, 16); cp <= end; cp++)
|
|
39
|
+
c.set(cp, Number(k));
|
|
40
|
+
}
|
|
41
|
+
const d = new Map();
|
|
42
|
+
for (const e of unicode_data_js_1.DECOMP.split(';')) {
|
|
43
|
+
const [cp, seq] = e.split(':');
|
|
44
|
+
d.set(parseInt(cp, 16), seq.split('.').map((h) => parseInt(h, 16)));
|
|
45
|
+
}
|
|
46
|
+
const m = new Map();
|
|
47
|
+
for (const e of unicode_data_js_1.COMP.split(';')) {
|
|
48
|
+
const [pair, cp] = e.split(':');
|
|
49
|
+
const [x, y] = pair.split('.').map((h) => parseInt(h, 16));
|
|
50
|
+
m.set(x * 0x110000 + y, parseInt(cp, 16));
|
|
51
|
+
}
|
|
52
|
+
cccMap = c;
|
|
53
|
+
decompMap = d;
|
|
54
|
+
compMap = m;
|
|
55
|
+
}
|
|
56
|
+
/** Canonical combining class (Unicode 15.1). */
|
|
57
|
+
function combiningClass(cp) {
|
|
58
|
+
tables();
|
|
59
|
+
return cccMap.get(cp) ?? 0;
|
|
60
|
+
}
|
|
61
|
+
const SBASE = 0xac00;
|
|
62
|
+
const LBASE = 0x1100;
|
|
63
|
+
const VBASE = 0x1161;
|
|
64
|
+
const TBASE = 0x11a7;
|
|
65
|
+
const LCOUNT = 19;
|
|
66
|
+
const VCOUNT = 21;
|
|
67
|
+
const TCOUNT = 28;
|
|
68
|
+
const NCOUNT = VCOUNT * TCOUNT;
|
|
69
|
+
const SCOUNT = LCOUNT * NCOUNT;
|
|
70
|
+
function decompose(cp, out) {
|
|
71
|
+
const s = cp - SBASE;
|
|
72
|
+
if (s >= 0 && s < SCOUNT) {
|
|
73
|
+
out.push(LBASE + Math.floor(s / NCOUNT), VBASE + Math.floor((s % NCOUNT) / TCOUNT));
|
|
74
|
+
if (s % TCOUNT)
|
|
75
|
+
out.push(TBASE + (s % TCOUNT));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const d = decompMap.get(cp);
|
|
79
|
+
if (d)
|
|
80
|
+
out.push(...d);
|
|
81
|
+
else
|
|
82
|
+
out.push(cp);
|
|
83
|
+
}
|
|
84
|
+
function compose(a, b) {
|
|
85
|
+
const l = a - LBASE;
|
|
86
|
+
if (l >= 0 && l < LCOUNT) {
|
|
87
|
+
const v = b - VBASE;
|
|
88
|
+
if (v >= 0 && v < VCOUNT)
|
|
89
|
+
return SBASE + (l * VCOUNT + v) * TCOUNT;
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const s = a - SBASE;
|
|
93
|
+
if (s >= 0 && s < SCOUNT && s % TCOUNT === 0) {
|
|
94
|
+
const t = b - TBASE;
|
|
95
|
+
if (t > 0 && t < TCOUNT)
|
|
96
|
+
return a + t;
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return compMap.get(a * 0x110000 + b);
|
|
100
|
+
}
|
|
101
|
+
/** Normalization Form C of a code point sequence (UAX #15), using Unicode 15.1 data. */
|
|
102
|
+
function nfc(cps) {
|
|
103
|
+
tables();
|
|
104
|
+
const d = [];
|
|
105
|
+
for (const cp of cps)
|
|
106
|
+
decompose(cp, d);
|
|
107
|
+
// Canonical ordering: stable sort of each run of non-starters by combining class.
|
|
108
|
+
for (let i = 1; i < d.length; i++) {
|
|
109
|
+
const cc = combiningClass(d[i]);
|
|
110
|
+
if (cc === 0)
|
|
111
|
+
continue;
|
|
112
|
+
let j = i;
|
|
113
|
+
while (j > 0) {
|
|
114
|
+
const pc = combiningClass(d[j - 1]);
|
|
115
|
+
if (pc <= cc)
|
|
116
|
+
break;
|
|
117
|
+
const t = d[j];
|
|
118
|
+
d[j] = d[j - 1];
|
|
119
|
+
d[j - 1] = t;
|
|
120
|
+
j--;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Canonical composition.
|
|
124
|
+
const res = [];
|
|
125
|
+
let starter = -1;
|
|
126
|
+
let lastCC = 0;
|
|
127
|
+
for (const ch of d) {
|
|
128
|
+
const cc = combiningClass(ch);
|
|
129
|
+
if (starter >= 0) {
|
|
130
|
+
const adjacent = res.length - 1 === starter;
|
|
131
|
+
if (adjacent || (lastCC !== 0 && lastCC < cc)) {
|
|
132
|
+
const c = compose(res[starter], ch);
|
|
133
|
+
if (c !== undefined) {
|
|
134
|
+
res[starter] = c;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (cc === 0)
|
|
140
|
+
starter = res.length;
|
|
141
|
+
lastCC = cc;
|
|
142
|
+
res.push(ch);
|
|
143
|
+
}
|
|
144
|
+
return res;
|
|
145
|
+
}
|
|
146
|
+
/** Code points of a string (lone surrogates come out as themselves). */
|
|
147
|
+
function codePoints(s) {
|
|
148
|
+
const out = [];
|
|
149
|
+
for (let i = 0; i < s.length; i++) {
|
|
150
|
+
const c = s.charCodeAt(i);
|
|
151
|
+
if (c >= 0xd800 && c <= 0xdbff && i + 1 < s.length) {
|
|
152
|
+
const d = s.charCodeAt(i + 1);
|
|
153
|
+
if (d >= 0xdc00 && d <= 0xdfff) {
|
|
154
|
+
out.push(0x10000 + ((c - 0xd800) << 10) + (d - 0xdc00));
|
|
155
|
+
i++;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
out.push(c);
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
/** True when the string is in NFC under Unicode 15.1. */
|
|
164
|
+
function isNFC(s) {
|
|
165
|
+
const cps = codePoints(s);
|
|
166
|
+
// Below U+0300 every character is a starter with NFC_Quick_Check=Yes.
|
|
167
|
+
if (cps.every((c) => c < 0x300))
|
|
168
|
+
return true;
|
|
169
|
+
const n = nfc(cps);
|
|
170
|
+
if (n.length !== cps.length)
|
|
171
|
+
return false;
|
|
172
|
+
for (let i = 0; i < n.length; i++)
|
|
173
|
+
if (n[i] !== cps[i])
|
|
174
|
+
return false;
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
/** True when the string contains an unpaired surrogate. */
|
|
178
|
+
function hasLoneSurrogate(s) {
|
|
179
|
+
for (let i = 0; i < s.length; i++) {
|
|
180
|
+
const c = s.charCodeAt(i);
|
|
181
|
+
if (c >= 0xd800 && c <= 0xdbff) {
|
|
182
|
+
const d = i + 1 < s.length ? s.charCodeAt(i + 1) : 0;
|
|
183
|
+
if (d >= 0xdc00 && d <= 0xdfff)
|
|
184
|
+
i++;
|
|
185
|
+
else
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
else if (c >= 0xdc00 && c <= 0xdfff)
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type TypeName } from './lexical.js';
|
|
2
|
+
/** A typed scalar (SPEC 4.1). */
|
|
3
|
+
export interface TypedScalar {
|
|
4
|
+
type: TypeName;
|
|
5
|
+
value: string | boolean;
|
|
6
|
+
}
|
|
7
|
+
/** A cell reference (SPEC 4.3): what a cell URI denotes. `"*"` marks a slice. */
|
|
8
|
+
export interface CellRef {
|
|
9
|
+
args: Record<string, TypedScalar | '*'>;
|
|
10
|
+
fn: string;
|
|
11
|
+
ns: string;
|
|
12
|
+
asof?: string;
|
|
13
|
+
}
|
|
14
|
+
/** A function descriptor's `signature` member (SPEC 7). */
|
|
15
|
+
export interface Signature {
|
|
16
|
+
args: Record<string, {
|
|
17
|
+
type: TypeName;
|
|
18
|
+
valid: boolean;
|
|
19
|
+
}>;
|
|
20
|
+
returns: TypeName;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Parse a cell URI into a cell reference, taking argument types from the function's
|
|
24
|
+
* signature (SPEC 5.4). Checks run in the order of SPEC 5.4 and the first failure is thrown as
|
|
25
|
+
* a DatumeshError.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseUri(uri: string, signature: Signature | unknown): CellRef;
|
|
28
|
+
/** Write the unique cell URI of a cell reference (SPEC 5.2). Throws DatumeshError if invalid. */
|
|
29
|
+
export declare function formatUri(ref: CellRef | unknown): string;
|
|
30
|
+
/** The HTTPS URL of a cell URI on mesh host `mesh` (SPEC 5.3). */
|
|
31
|
+
export declare function httpsUrl(uri: string, mesh: string): string;
|
package/dist/cjs/uri.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseUri = parseUri;
|
|
4
|
+
exports.formatUri = formatUri;
|
|
5
|
+
exports.httpsUrl = httpsUrl;
|
|
6
|
+
// Cell URIs (SPEC.md section 5).
|
|
7
|
+
const errors_js_1 = require("./errors.js");
|
|
8
|
+
const json_js_1 = require("./json.js");
|
|
9
|
+
const lexical_js_1 = require("./lexical.js");
|
|
10
|
+
const utf8_js_1 = require("./utf8.js");
|
|
11
|
+
const validate_js_1 = require("./validate.js");
|
|
12
|
+
const SCHEME = 'cell://';
|
|
13
|
+
function isLiteral(c) {
|
|
14
|
+
return /^[A-Za-z0-9\-._~:]$/.test(c);
|
|
15
|
+
}
|
|
16
|
+
function decodeValue(v, path) {
|
|
17
|
+
const bytes = [];
|
|
18
|
+
for (let i = 0; i < v.length;) {
|
|
19
|
+
const c = v[i];
|
|
20
|
+
if (c === '%') {
|
|
21
|
+
const h = v.slice(i + 1, i + 3);
|
|
22
|
+
if (!/^[0-9A-F]{2}$/.test(h))
|
|
23
|
+
(0, errors_js_1.fail)('E_CANON_PERCENT', 'percent-encoding must be % and two uppercase hex digits', path);
|
|
24
|
+
const b = parseInt(h, 16);
|
|
25
|
+
if (isLiteral(String.fromCharCode(b)))
|
|
26
|
+
(0, errors_js_1.fail)('E_CANON_PERCENT', `${JSON.stringify(String.fromCharCode(b))} must not be percent-encoded`, path);
|
|
27
|
+
bytes.push(b);
|
|
28
|
+
i += 3;
|
|
29
|
+
}
|
|
30
|
+
else if (isLiteral(c)) {
|
|
31
|
+
bytes.push(c.charCodeAt(0));
|
|
32
|
+
i++;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const cp = v.codePointAt(i);
|
|
36
|
+
(0, errors_js_1.fail)('E_CANON_PERCENT', `${JSON.stringify(String.fromCodePoint(cp))} must be percent-encoded`, path);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const s = (0, utf8_js_1.utf8Decode)(Uint8Array.from(bytes));
|
|
40
|
+
if (s === null)
|
|
41
|
+
(0, errors_js_1.fail)('E_CANON_PERCENT', 'percent-encoded bytes are not UTF-8', path);
|
|
42
|
+
return s;
|
|
43
|
+
}
|
|
44
|
+
function encodeValue(s) {
|
|
45
|
+
let out = '';
|
|
46
|
+
for (const b of (0, utf8_js_1.utf8Encode)(s)) {
|
|
47
|
+
const c = String.fromCharCode(b);
|
|
48
|
+
out += isLiteral(c) ? c : `%${b.toString(16).toUpperCase().padStart(2, '0')}`;
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function readSignature(signature) {
|
|
53
|
+
if (signature === null || signature === undefined)
|
|
54
|
+
throw new TypeError('parseUri needs the function signature (descriptor.signature)');
|
|
55
|
+
let sig;
|
|
56
|
+
try {
|
|
57
|
+
sig = (0, json_js_1.toJson)(signature);
|
|
58
|
+
(0, validate_js_1.structSignature)(sig, 'signature');
|
|
59
|
+
(0, validate_js_1.canonSignature)(sig, 'signature');
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
if (e instanceof errors_js_1.DatumeshError)
|
|
63
|
+
throw new errors_js_1.DatumeshError(e.code, `invalid signature: ${e.detail}`, e.path?.replace(/^\$/, 'signature') ?? 'signature');
|
|
64
|
+
throw e;
|
|
65
|
+
}
|
|
66
|
+
return sig;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Parse a cell URI into a cell reference, taking argument types from the function's
|
|
70
|
+
* signature (SPEC 5.4). Checks run in the order of SPEC 5.4 and the first failure is thrown as
|
|
71
|
+
* a DatumeshError.
|
|
72
|
+
*/
|
|
73
|
+
function parseUri(uri, signature) {
|
|
74
|
+
if (typeof uri !== 'string')
|
|
75
|
+
throw new TypeError('uri must be a string');
|
|
76
|
+
const sig = readSignature(signature);
|
|
77
|
+
const declared = sig.args;
|
|
78
|
+
// 1. Outer grammar.
|
|
79
|
+
if (!uri.startsWith(SCHEME) || uri.includes('#'))
|
|
80
|
+
(0, errors_js_1.fail)('E_PARSE_URI', 'not cell://<namespace>/<function>[?<query>] without fragment', '$');
|
|
81
|
+
const rest = uri.slice(SCHEME.length);
|
|
82
|
+
const q = rest.indexOf('?');
|
|
83
|
+
const path = q < 0 ? rest : rest.slice(0, q);
|
|
84
|
+
const query = q < 0 ? null : rest.slice(q + 1);
|
|
85
|
+
if (query === '')
|
|
86
|
+
(0, errors_js_1.fail)('E_PARSE_URI', 'empty query', '$');
|
|
87
|
+
// SPEC 5.1 lists userinfo and a port among outer-grammar failures (E_PARSE_URI). (The
|
|
88
|
+
// reference tool reports them as E_CANON_NAME; see DEFECTS.md.)
|
|
89
|
+
const authority = path.split('/')[0];
|
|
90
|
+
if (authority.includes('@'))
|
|
91
|
+
(0, errors_js_1.fail)('E_PARSE_URI', 'a cell URI has no userinfo', '$');
|
|
92
|
+
if (authority.includes(':'))
|
|
93
|
+
(0, errors_js_1.fail)('E_PARSE_URI', 'a cell URI has no port', '$');
|
|
94
|
+
const params = (query === null ? [] : query.split('&')).map((p) => {
|
|
95
|
+
const e = p.indexOf('=');
|
|
96
|
+
if (e < 0)
|
|
97
|
+
(0, errors_js_1.fail)('E_PARSE_URI', `parameter without '=': ${JSON.stringify(p)}`, '$');
|
|
98
|
+
return { name: p.slice(0, e), value: p.slice(e + 1) };
|
|
99
|
+
});
|
|
100
|
+
// 2. Namespace and function name.
|
|
101
|
+
const slash = path.lastIndexOf('/');
|
|
102
|
+
if (slash < 0)
|
|
103
|
+
(0, errors_js_1.fail)('E_CANON_NAME', 'missing namespace', 'uri.path');
|
|
104
|
+
const ns = path.slice(0, slash);
|
|
105
|
+
const fn = path.slice(slash + 1);
|
|
106
|
+
(0, lexical_js_1.checkNamespace)(ns, 'uri.path');
|
|
107
|
+
(0, lexical_js_1.checkName)(fn, 'uri.path');
|
|
108
|
+
if (path.length > lexical_js_1.LIMITS.function_name)
|
|
109
|
+
(0, errors_js_1.fail)('E_CANON_LIMIT', 'function name too long', 'uri.path');
|
|
110
|
+
// 3. Parameter names, left to right.
|
|
111
|
+
const at = (n) => `uri.query.${n}`;
|
|
112
|
+
// asof: when the last parameter is asof, that is the asof parameter and any earlier asof is a
|
|
113
|
+
// repetition (E_CANON_ARG_DUPLICATE); otherwise an asof anywhere is out of place
|
|
114
|
+
// (E_CANON_ARG_ORDER). SPEC 5.2 does not say which applies when both do; this matches the
|
|
115
|
+
// reference tool (DEFECTS.md).
|
|
116
|
+
let prev = null;
|
|
117
|
+
const lastIsAsof = params.length > 0 && params[params.length - 1].name === 'asof';
|
|
118
|
+
params.forEach((p, i) => {
|
|
119
|
+
if (p.name === 'asof') {
|
|
120
|
+
if (!lastIsAsof)
|
|
121
|
+
(0, errors_js_1.fail)('E_CANON_ARG_ORDER', 'asof appears once, as the last parameter', at(p.name));
|
|
122
|
+
if (i !== params.length - 1)
|
|
123
|
+
(0, errors_js_1.fail)('E_CANON_ARG_DUPLICATE', 'asof repeated', at(p.name));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
(0, lexical_js_1.checkArgName)(p.name, at(p.name));
|
|
127
|
+
if (prev !== null && p.name === prev)
|
|
128
|
+
(0, errors_js_1.fail)('E_CANON_ARG_DUPLICATE', `argument ${JSON.stringify(p.name)} repeated`, at(p.name));
|
|
129
|
+
if (prev !== null && p.name < prev)
|
|
130
|
+
(0, errors_js_1.fail)('E_CANON_ARG_ORDER', 'arguments must be sorted by name', at(p.name));
|
|
131
|
+
prev = p.name;
|
|
132
|
+
});
|
|
133
|
+
const argParams = params.filter((p) => p.name !== 'asof');
|
|
134
|
+
// 4. Argument count.
|
|
135
|
+
if (argParams.length > lexical_js_1.LIMITS.args)
|
|
136
|
+
(0, errors_js_1.fail)('E_CANON_LIMIT', `more than ${lexical_js_1.LIMITS.args} arguments`, 'uri.query');
|
|
137
|
+
// 5. Against the signature: unknown arguments, then missing ones.
|
|
138
|
+
for (const p of argParams) {
|
|
139
|
+
if (!Object.prototype.hasOwnProperty.call(declared, p.name))
|
|
140
|
+
(0, errors_js_1.fail)('E_ARG_UNKNOWN', `function declares no argument ${JSON.stringify(p.name)}`, at(p.name));
|
|
141
|
+
}
|
|
142
|
+
const present = new Set(argParams.map((p) => p.name));
|
|
143
|
+
for (const n of (0, json_js_1.sortedKeys)(declared)) {
|
|
144
|
+
if (!present.has(n))
|
|
145
|
+
(0, errors_js_1.fail)('E_ARG_MISSING', `argument ${JSON.stringify(n)} absent; use ${n}=* for a slice`, 'uri.query');
|
|
146
|
+
}
|
|
147
|
+
// 6. Values, left to right: percent-encoding, then the lexical form.
|
|
148
|
+
const args = {};
|
|
149
|
+
for (const p of argParams) {
|
|
150
|
+
if (p.value === '*') {
|
|
151
|
+
args[p.name] = '*';
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const type = declared[p.name].type;
|
|
155
|
+
const s = decodeValue(p.value, at(p.name));
|
|
156
|
+
if (type === 'boolean') {
|
|
157
|
+
if (s !== 'true' && s !== 'false')
|
|
158
|
+
(0, errors_js_1.fail)('E_CANON_TYPE', 'boolean must be true or false', at(p.name));
|
|
159
|
+
args[p.name] = { type, value: s === 'true' };
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
(0, lexical_js_1.checkTypedValue)(type, s, at(p.name));
|
|
163
|
+
args[p.name] = { type, value: s };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// 7. asof.
|
|
167
|
+
const ref = { args, fn, ns };
|
|
168
|
+
const asofParam = params.find((p) => p.name === 'asof');
|
|
169
|
+
if (asofParam) {
|
|
170
|
+
const s = decodeValue(asofParam.value, at('asof'));
|
|
171
|
+
(0, lexical_js_1.checkInstant)(s, at('asof'));
|
|
172
|
+
ref.asof = s;
|
|
173
|
+
}
|
|
174
|
+
return ref;
|
|
175
|
+
}
|
|
176
|
+
/** Write the unique cell URI of a cell reference (SPEC 5.2). Throws DatumeshError if invalid. */
|
|
177
|
+
function formatUri(ref) {
|
|
178
|
+
const v = (0, json_js_1.toJson)(ref);
|
|
179
|
+
(0, validate_js_1.validate)(v, 'ref');
|
|
180
|
+
const o = v;
|
|
181
|
+
const args = o.args;
|
|
182
|
+
const parts = [];
|
|
183
|
+
for (const k of (0, json_js_1.sortedKeys)(args)) {
|
|
184
|
+
const a = args[k];
|
|
185
|
+
if (a === '*')
|
|
186
|
+
parts.push(`${k}=*`);
|
|
187
|
+
else if ((0, json_js_1.isObject)(a) && typeof a.value === 'boolean')
|
|
188
|
+
parts.push(`${k}=${a.value ? 'true' : 'false'}`);
|
|
189
|
+
else
|
|
190
|
+
parts.push(`${k}=${encodeValue(a.value)}`);
|
|
191
|
+
}
|
|
192
|
+
if (typeof o.asof === 'string')
|
|
193
|
+
parts.push(`asof=${encodeValue(o.asof)}`);
|
|
194
|
+
return `${SCHEME}${o.ns}/${o.fn}${parts.length ? `?${parts.join('&')}` : ''}`;
|
|
195
|
+
}
|
|
196
|
+
/** The HTTPS URL of a cell URI on mesh host `mesh` (SPEC 5.3). */
|
|
197
|
+
function httpsUrl(uri, mesh) {
|
|
198
|
+
if (!uri.startsWith(SCHEME))
|
|
199
|
+
(0, errors_js_1.fail)('E_PARSE_URI', 'not a cell URI', '$');
|
|
200
|
+
return `https://${mesh}/cell/${uri.slice(SCHEME.length)}`;
|
|
201
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Encode a well-formed string as UTF-8. Lone surrogates must have been rejected already. */
|
|
2
|
+
export declare function utf8Encode(s: string): Uint8Array;
|
|
3
|
+
/** Number of UTF-8 bytes in a well-formed string. */
|
|
4
|
+
export declare function utf8Length(s: string): number;
|
|
5
|
+
/**
|
|
6
|
+
* Decode strict UTF-8 (RFC 3629): no overlong forms, no surrogates, nothing above U+10FFFF,
|
|
7
|
+
* no truncated sequences. Returns null when the bytes are not UTF-8. A leading BOM is kept
|
|
8
|
+
* (as U+FEFF) so the caller can reject it.
|
|
9
|
+
*/
|
|
10
|
+
export declare function utf8Decode(b: Uint8Array): string | null;
|
package/dist/cjs/utf8.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Strict UTF-8 encoding and decoding, written out so results never depend on the host's
|
|
3
|
+
// TextDecoder (BOM handling, replacement characters) or its ICU build.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.utf8Encode = utf8Encode;
|
|
6
|
+
exports.utf8Length = utf8Length;
|
|
7
|
+
exports.utf8Decode = utf8Decode;
|
|
8
|
+
/** Encode a well-formed string as UTF-8. Lone surrogates must have been rejected already. */
|
|
9
|
+
function utf8Encode(s) {
|
|
10
|
+
const out = [];
|
|
11
|
+
for (let i = 0; i < s.length; i++) {
|
|
12
|
+
let c = s.charCodeAt(i);
|
|
13
|
+
if (c >= 0xd800 && c <= 0xdbff && i + 1 < s.length) {
|
|
14
|
+
const d = s.charCodeAt(i + 1);
|
|
15
|
+
if (d >= 0xdc00 && d <= 0xdfff) {
|
|
16
|
+
c = 0x10000 + ((c - 0xd800) << 10) + (d - 0xdc00);
|
|
17
|
+
i++;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (c < 0x80)
|
|
21
|
+
out.push(c);
|
|
22
|
+
else if (c < 0x800)
|
|
23
|
+
out.push(0xc0 | (c >> 6), 0x80 | (c & 63));
|
|
24
|
+
else if (c < 0x10000)
|
|
25
|
+
out.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 63), 0x80 | (c & 63));
|
|
26
|
+
else
|
|
27
|
+
out.push(0xf0 | (c >> 18), 0x80 | ((c >> 12) & 63), 0x80 | ((c >> 6) & 63), 0x80 | (c & 63));
|
|
28
|
+
}
|
|
29
|
+
return Uint8Array.from(out);
|
|
30
|
+
}
|
|
31
|
+
/** Number of UTF-8 bytes in a well-formed string. */
|
|
32
|
+
function utf8Length(s) {
|
|
33
|
+
let n = 0;
|
|
34
|
+
for (let i = 0; i < s.length; i++) {
|
|
35
|
+
const c = s.charCodeAt(i);
|
|
36
|
+
if (c < 0x80)
|
|
37
|
+
n += 1;
|
|
38
|
+
else if (c < 0x800)
|
|
39
|
+
n += 2;
|
|
40
|
+
else if (c >= 0xd800 && c <= 0xdbff) {
|
|
41
|
+
n += 4;
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
else
|
|
45
|
+
n += 3;
|
|
46
|
+
}
|
|
47
|
+
return n;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Decode strict UTF-8 (RFC 3629): no overlong forms, no surrogates, nothing above U+10FFFF,
|
|
51
|
+
* no truncated sequences. Returns null when the bytes are not UTF-8. A leading BOM is kept
|
|
52
|
+
* (as U+FEFF) so the caller can reject it.
|
|
53
|
+
*/
|
|
54
|
+
function utf8Decode(b) {
|
|
55
|
+
let out = '';
|
|
56
|
+
const parts = [];
|
|
57
|
+
for (let i = 0; i < b.length;) {
|
|
58
|
+
const x = b[i];
|
|
59
|
+
let cp;
|
|
60
|
+
let need;
|
|
61
|
+
let min;
|
|
62
|
+
if (x < 0x80) {
|
|
63
|
+
cp = x;
|
|
64
|
+
need = 0;
|
|
65
|
+
min = 0;
|
|
66
|
+
}
|
|
67
|
+
else if (x >= 0xc2 && x <= 0xdf) {
|
|
68
|
+
cp = x & 0x1f;
|
|
69
|
+
need = 1;
|
|
70
|
+
min = 0x80;
|
|
71
|
+
}
|
|
72
|
+
else if (x >= 0xe0 && x <= 0xef) {
|
|
73
|
+
cp = x & 0x0f;
|
|
74
|
+
need = 2;
|
|
75
|
+
min = 0x800;
|
|
76
|
+
}
|
|
77
|
+
else if (x >= 0xf0 && x <= 0xf4) {
|
|
78
|
+
cp = x & 0x07;
|
|
79
|
+
need = 3;
|
|
80
|
+
min = 0x10000;
|
|
81
|
+
}
|
|
82
|
+
else
|
|
83
|
+
return null;
|
|
84
|
+
for (let k = 1; k <= need; k++) {
|
|
85
|
+
const y = b[i + k];
|
|
86
|
+
if (y === undefined || (y & 0xc0) !== 0x80)
|
|
87
|
+
return null;
|
|
88
|
+
cp = (cp << 6) | (y & 0x3f);
|
|
89
|
+
}
|
|
90
|
+
if (cp < min || cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff))
|
|
91
|
+
return null;
|
|
92
|
+
out += String.fromCodePoint(cp);
|
|
93
|
+
if (out.length > 8192) {
|
|
94
|
+
parts.push(out);
|
|
95
|
+
out = '';
|
|
96
|
+
}
|
|
97
|
+
i += need + 1;
|
|
98
|
+
}
|
|
99
|
+
parts.push(out);
|
|
100
|
+
return parts.join('');
|
|
101
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type JsonObject, type JsonValue } from './json.js';
|
|
2
|
+
export type Kind = 'scalar' | 'cell' | 'ref' | 'function' | 'receipt' | 'jcs';
|
|
3
|
+
export declare const KINDS: readonly Kind[];
|
|
4
|
+
export declare function structSignature(v: JsonValue, path: string): void;
|
|
5
|
+
export declare function canonSignature(sig: JsonObject, path: string): void;
|
|
6
|
+
/** Validate `v` as `kind` (steps 2-3; for receipts also step 4). Throws DatumeshError. */
|
|
7
|
+
export declare function validate(v: JsonValue, kind: Kind, path?: string): void;
|
|
8
|
+
/** Receipt step 2 only: true when the receipt has the right members and JSON types. */
|
|
9
|
+
export declare function receiptStructure(v: JsonValue): void;
|
|
10
|
+
/** Receipt steps 3 and 4, after receiptStructure has passed. */
|
|
11
|
+
export declare function receiptCanonicalAndConsistent(v: JsonValue): void;
|
|
12
|
+
/**
|
|
13
|
+
* Infer the kind of an object from its members when the caller gives none: a receipt has
|
|
14
|
+
* `sig`, `proto` or `computed_by`; a descriptor `signature` or `code`; a cell `fn_hash`; a cell
|
|
15
|
+
* reference `fn`, `ns` or `args`; a typed scalar `type` or `value`. Anything else is E_SCHEMA.
|
|
16
|
+
*/
|
|
17
|
+
export declare function inferKind(v: JsonValue): Exclude<Kind, 'jcs'>;
|