@fedify/vocab-runtime 2.0.0-pr.451.1730
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 +20 -0
- package/README.md +26 -0
- package/deno.json +29 -0
- package/dist/mod.cjs +5183 -0
- package/dist/mod.d.cts +274 -0
- package/dist/mod.d.ts +276 -0
- package/dist/mod.js +5149 -0
- package/package.json +52 -0
- package/src/contexts.ts +4237 -0
- package/src/docloader.test.ts +365 -0
- package/src/docloader.ts +311 -0
- package/src/jwk.ts +70 -0
- package/src/key.test.ts +179 -0
- package/src/key.ts +187 -0
- package/src/langstr.test.ts +28 -0
- package/src/langstr.ts +38 -0
- package/src/link.test.ts +82 -0
- package/src/link.ts +345 -0
- package/src/mod.ts +31 -0
- package/src/multibase/base.ts +34 -0
- package/src/multibase/constants.ts +89 -0
- package/src/multibase/mod.ts +82 -0
- package/src/multibase/multibase.test.ts +117 -0
- package/src/multibase/rfc4648.ts +103 -0
- package/src/multibase/types.d.ts +61 -0
- package/src/multibase/util.ts +22 -0
- package/src/request.ts +115 -0
- package/src/url.test.ts +59 -0
- package/src/url.ts +96 -0
- package/tsdown.config.ts +9 -0
package/src/link.ts
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
// Borrowed from https://github.com/hugoalh/http-header-link-es
|
|
2
|
+
const parametersNeedLowerCase: readonly string[] = ["rel", "type"];
|
|
3
|
+
|
|
4
|
+
const regexpLinkWhitespace = /[\n\r\s\t]/;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* HTTP header `Link` entry.
|
|
8
|
+
*/
|
|
9
|
+
export type HttpHeaderLinkEntry = [
|
|
10
|
+
uri: string,
|
|
11
|
+
parameters: { [key: string]: string },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
function validateURI(uri: string): void {
|
|
15
|
+
if (uri.includes("\n") || regexpLinkWhitespace.test(uri)) {
|
|
16
|
+
throw new SyntaxError(`\`${uri}\` is not a valid URI!`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function* parseLinkFromString(input: string): Generator<HttpHeaderLinkEntry> {
|
|
21
|
+
// Remove Unicode characters of BOM (Byte Order Mark) and no-break space.
|
|
22
|
+
const inputFmt: string = input.replaceAll("\u00A0", "").replaceAll(
|
|
23
|
+
"\uFEFF",
|
|
24
|
+
"",
|
|
25
|
+
);
|
|
26
|
+
for (let cursor: number = 0; cursor < inputFmt.length; cursor += 1) {
|
|
27
|
+
while (regexpLinkWhitespace.test(inputFmt.charAt(cursor))) {
|
|
28
|
+
cursor += 1;
|
|
29
|
+
}
|
|
30
|
+
if (inputFmt.charAt(cursor) !== "<") {
|
|
31
|
+
throw new SyntaxError(
|
|
32
|
+
`Unexpected character \`${
|
|
33
|
+
inputFmt.charAt(cursor)
|
|
34
|
+
}\` at position ${cursor}; Expect character \`<\`!`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
cursor += 1;
|
|
38
|
+
const cursorEndUri: number = inputFmt.indexOf(">", cursor);
|
|
39
|
+
if (cursorEndUri === -1) {
|
|
40
|
+
throw new SyntaxError(
|
|
41
|
+
`Missing end of URI delimiter character \`>\` after position ${cursor}!`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (cursorEndUri === cursor) {
|
|
45
|
+
throw new SyntaxError(`Missing URI at position ${cursor}!`);
|
|
46
|
+
}
|
|
47
|
+
const uriSlice: string = inputFmt.slice(cursor, cursorEndUri);
|
|
48
|
+
validateURI(uriSlice);
|
|
49
|
+
const uri: HttpHeaderLinkEntry[0] = decodeURI(uriSlice);
|
|
50
|
+
const parameters: HttpHeaderLinkEntry[1] = {};
|
|
51
|
+
cursor = cursorEndUri + 1;
|
|
52
|
+
while (regexpLinkWhitespace.test(inputFmt.charAt(cursor))) {
|
|
53
|
+
cursor += 1;
|
|
54
|
+
}
|
|
55
|
+
if (
|
|
56
|
+
cursor === inputFmt.length ||
|
|
57
|
+
inputFmt.charAt(cursor) === ","
|
|
58
|
+
) {
|
|
59
|
+
yield [uri, parameters];
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (inputFmt.charAt(cursor) !== ";") {
|
|
63
|
+
throw new SyntaxError(
|
|
64
|
+
`Unexpected character \`${
|
|
65
|
+
inputFmt.charAt(cursor)
|
|
66
|
+
}\` at position ${cursor}; Expect character \`;\`!`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
cursor += 1;
|
|
70
|
+
while (cursor < inputFmt.length) {
|
|
71
|
+
while (regexpLinkWhitespace.test(inputFmt.charAt(cursor))) {
|
|
72
|
+
cursor += 1;
|
|
73
|
+
}
|
|
74
|
+
const parameterKey: string | undefined = inputFmt.slice(cursor).match(
|
|
75
|
+
/^[\w-]+\*?/,
|
|
76
|
+
)?.[0].toLowerCase();
|
|
77
|
+
if (typeof parameterKey === "undefined") {
|
|
78
|
+
throw new SyntaxError(
|
|
79
|
+
`Unexpected character \`${
|
|
80
|
+
inputFmt.charAt(cursor)
|
|
81
|
+
}\` at position ${cursor}; Expect a valid parameter key!`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
cursor += parameterKey.length;
|
|
85
|
+
while (regexpLinkWhitespace.test(inputFmt.charAt(cursor))) {
|
|
86
|
+
cursor += 1;
|
|
87
|
+
}
|
|
88
|
+
if (
|
|
89
|
+
cursor === inputFmt.length ||
|
|
90
|
+
inputFmt.charAt(cursor) === ","
|
|
91
|
+
) {
|
|
92
|
+
parameters[parameterKey] = "";
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
if (inputFmt.charAt(cursor) === ";") {
|
|
96
|
+
parameters[parameterKey] = "";
|
|
97
|
+
cursor += 1;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (inputFmt.charAt(cursor) !== "=") {
|
|
101
|
+
throw new SyntaxError(
|
|
102
|
+
`Unexpected character \`${
|
|
103
|
+
inputFmt.charAt(cursor)
|
|
104
|
+
}\` at position ${cursor}; Expect character \`=\`!`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
cursor += 1;
|
|
108
|
+
while (regexpLinkWhitespace.test(inputFmt.charAt(cursor))) {
|
|
109
|
+
cursor += 1;
|
|
110
|
+
}
|
|
111
|
+
let parameterValue: string = "";
|
|
112
|
+
if (inputFmt.charAt(cursor) === '"') {
|
|
113
|
+
cursor += 1;
|
|
114
|
+
while (cursor < inputFmt.length) {
|
|
115
|
+
if (inputFmt.charAt(cursor) === '"') {
|
|
116
|
+
cursor += 1;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
if (inputFmt.charAt(cursor) === "\\") {
|
|
120
|
+
cursor += 1;
|
|
121
|
+
}
|
|
122
|
+
parameterValue += inputFmt.charAt(cursor);
|
|
123
|
+
cursor += 1;
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
const cursorDiffParameterValue: number = inputFmt.slice(cursor).search(
|
|
127
|
+
/[\s;,]/,
|
|
128
|
+
);
|
|
129
|
+
if (cursorDiffParameterValue === -1) {
|
|
130
|
+
parameterValue += inputFmt.slice(cursor);
|
|
131
|
+
cursor += parameterValue.length;
|
|
132
|
+
} else {
|
|
133
|
+
parameterValue += inputFmt.slice(cursor, cursorDiffParameterValue);
|
|
134
|
+
cursor += cursorDiffParameterValue;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
parameters[parameterKey] = parametersNeedLowerCase.includes(parameterKey)
|
|
138
|
+
? parameterValue.toLowerCase()
|
|
139
|
+
: parameterValue;
|
|
140
|
+
while (regexpLinkWhitespace.test(inputFmt.charAt(cursor))) {
|
|
141
|
+
cursor += 1;
|
|
142
|
+
}
|
|
143
|
+
if (
|
|
144
|
+
cursor === inputFmt.length ||
|
|
145
|
+
inputFmt.charAt(cursor) === ","
|
|
146
|
+
) {
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
if (inputFmt.charAt(cursor) === ";") {
|
|
150
|
+
cursor += 1;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
throw new SyntaxError(
|
|
154
|
+
`Unexpected character \`${
|
|
155
|
+
inputFmt.charAt(cursor)
|
|
156
|
+
}\` at position ${cursor}; Expect character \`,\`, character \`;\`, or end of the string!`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
yield [uri, parameters];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Handle the HTTP header `Link` according to the specification RFC 8288.
|
|
165
|
+
*/
|
|
166
|
+
export class HttpHeaderLink {
|
|
167
|
+
get [Symbol.toStringTag](): string {
|
|
168
|
+
return "HTTPHeaderLink";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#entries: HttpHeaderLinkEntry[] = [];
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Handle the HTTP header `Link` according to the specification RFC 8288.
|
|
175
|
+
* @param {...(string | Headers | HttpHeaderLink | HttpHeaderLinkEntry[] | Response)} inputs Input.
|
|
176
|
+
*/
|
|
177
|
+
constructor(
|
|
178
|
+
...inputs:
|
|
179
|
+
(string | Headers | HttpHeaderLink | HttpHeaderLinkEntry[] | Response)[]
|
|
180
|
+
) {
|
|
181
|
+
if (inputs.length > 0) {
|
|
182
|
+
this.add(...inputs);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Add entries.
|
|
188
|
+
* @param {...(string | Headers | HttpHeaderLink | HttpHeaderLinkEntry[] | Response)} inputs Input.
|
|
189
|
+
* @returns {this}
|
|
190
|
+
*/
|
|
191
|
+
add(
|
|
192
|
+
...inputs:
|
|
193
|
+
(string | Headers | HttpHeaderLink | HttpHeaderLinkEntry[] | Response)[]
|
|
194
|
+
): this {
|
|
195
|
+
for (const input of inputs) {
|
|
196
|
+
if (input instanceof HttpHeaderLink) {
|
|
197
|
+
this.#entries.push(...structuredClone(input.#entries));
|
|
198
|
+
} else if (Array.isArray(input)) {
|
|
199
|
+
this.#entries.push(
|
|
200
|
+
...input.map(
|
|
201
|
+
([uri, parameters]: HttpHeaderLinkEntry): HttpHeaderLinkEntry => {
|
|
202
|
+
validateURI(uri);
|
|
203
|
+
Object.entries(parameters).forEach(
|
|
204
|
+
([key, value]: [string, string]): void => {
|
|
205
|
+
if (
|
|
206
|
+
key !== key.toLowerCase() ||
|
|
207
|
+
!(/^[\w-]+\*?$/.test(key))
|
|
208
|
+
) {
|
|
209
|
+
throw new SyntaxError(
|
|
210
|
+
`\`${key}\` is not a valid parameter key!`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
if (
|
|
214
|
+
parametersNeedLowerCase.includes(key) &&
|
|
215
|
+
value !== value.toLowerCase()
|
|
216
|
+
) {
|
|
217
|
+
throw new SyntaxError(
|
|
218
|
+
`\`${value}\` is not a valid parameter value!`,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
);
|
|
223
|
+
return [uri, structuredClone(parameters)];
|
|
224
|
+
},
|
|
225
|
+
),
|
|
226
|
+
);
|
|
227
|
+
} else {
|
|
228
|
+
for (
|
|
229
|
+
const entry of parseLinkFromString(
|
|
230
|
+
((
|
|
231
|
+
input instanceof Headers ||
|
|
232
|
+
input instanceof Response
|
|
233
|
+
)
|
|
234
|
+
? ((input instanceof Headers) ? input : input.headers).get("Link")
|
|
235
|
+
: input) ?? "",
|
|
236
|
+
)
|
|
237
|
+
) {
|
|
238
|
+
this.#entries.push(entry);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return this;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Return all of the entries.
|
|
247
|
+
* @returns {HttpHeaderLinkEntry[]} Entries.
|
|
248
|
+
*/
|
|
249
|
+
entries(): HttpHeaderLinkEntry[] {
|
|
250
|
+
return structuredClone(this.#entries);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Get entries by parameter.
|
|
255
|
+
* @param {string} key Key of the parameter.
|
|
256
|
+
* @param {string} value Value of the parameter.
|
|
257
|
+
* @returns {HttpHeaderLinkEntry[]} Entries which match the parameter.
|
|
258
|
+
*/
|
|
259
|
+
getByParameter(key: string, value: string): HttpHeaderLinkEntry[] {
|
|
260
|
+
if (key !== key.toLowerCase()) {
|
|
261
|
+
throw new SyntaxError(`\`${key}\` is not a valid parameter key!`);
|
|
262
|
+
}
|
|
263
|
+
if (key === "rel") {
|
|
264
|
+
return this.getByRel(value);
|
|
265
|
+
}
|
|
266
|
+
return structuredClone(
|
|
267
|
+
this.#entries.filter((entry: HttpHeaderLinkEntry): boolean => {
|
|
268
|
+
return (entry[1][key] === value);
|
|
269
|
+
}),
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Get entries by parameter `rel`.
|
|
275
|
+
* @param {string} value Value of the parameter `rel`.
|
|
276
|
+
* @returns {HttpHeaderLinkEntry[]} Entries which match the parameter.
|
|
277
|
+
*/
|
|
278
|
+
getByRel(value: string): HttpHeaderLinkEntry[] {
|
|
279
|
+
if (value !== value.toLowerCase()) {
|
|
280
|
+
throw new SyntaxError(
|
|
281
|
+
`\`${value}\` is not a valid parameter \`rel\` value!`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
return structuredClone(
|
|
285
|
+
this.#entries.filter((entity: HttpHeaderLinkEntry): boolean => {
|
|
286
|
+
return (entity[1].rel?.toLowerCase() === value);
|
|
287
|
+
}),
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Whether have entries that match parameter.
|
|
293
|
+
* @param {string} key Key of the parameter.
|
|
294
|
+
* @param {string} value Value of the parameter.
|
|
295
|
+
* @returns {boolean} Determine result.
|
|
296
|
+
*/
|
|
297
|
+
hasParameter(key: string, value: string): boolean {
|
|
298
|
+
return (this.getByParameter(key, value).length > 0);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Stringify entries.
|
|
303
|
+
* @returns {string} Stringified entries.
|
|
304
|
+
*/
|
|
305
|
+
toString(): string {
|
|
306
|
+
return this.#entries.map(
|
|
307
|
+
([uri, parameters]: HttpHeaderLinkEntry): string => {
|
|
308
|
+
return [
|
|
309
|
+
`<${encodeURI(uri)}>`,
|
|
310
|
+
...Object.entries(parameters).map(
|
|
311
|
+
([key, value]: [string, string]): string => {
|
|
312
|
+
return ((value.length > 0)
|
|
313
|
+
? `${key}="${value.replaceAll('"', '\\"')}"`
|
|
314
|
+
: key);
|
|
315
|
+
},
|
|
316
|
+
),
|
|
317
|
+
].join("; ");
|
|
318
|
+
},
|
|
319
|
+
).join(", ");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Parse the HTTP header `Link` according to the specification RFC 8288.
|
|
324
|
+
* @param {...(string | Headers | HttpHeaderLink | HttpHeaderLinkEntry[] | Response)} inputs Input.
|
|
325
|
+
* @returns {HttpHeaderLink}
|
|
326
|
+
*/
|
|
327
|
+
static parse(
|
|
328
|
+
...inputs:
|
|
329
|
+
(string | Headers | HttpHeaderLink | HttpHeaderLinkEntry[] | Response)[]
|
|
330
|
+
): HttpHeaderLink {
|
|
331
|
+
return new this(...inputs);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Stringify as the HTTP header `Link` according to the specification RFC 8288.
|
|
336
|
+
* @param {...(string | Headers | HttpHeaderLink | HttpHeaderLinkEntry[] | Response)} inputs Input.
|
|
337
|
+
* @returns {string}
|
|
338
|
+
*/
|
|
339
|
+
static stringify(
|
|
340
|
+
...inputs:
|
|
341
|
+
(string | Headers | HttpHeaderLink | HttpHeaderLinkEntry[] | Response)[]
|
|
342
|
+
): string {
|
|
343
|
+
return new this(...inputs).toString();
|
|
344
|
+
}
|
|
345
|
+
}
|
package/src/mod.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This package contains the runtime facilities for working with Activity
|
|
3
|
+
* Vocabulary objects, which are auto-generated from the IDL.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
export {
|
|
8
|
+
type AuthenticatedDocumentLoaderFactory,
|
|
9
|
+
type DocumentLoader,
|
|
10
|
+
type DocumentLoaderFactory,
|
|
11
|
+
type DocumentLoaderFactoryOptions,
|
|
12
|
+
type DocumentLoaderOptions,
|
|
13
|
+
getDocumentLoader,
|
|
14
|
+
type GetDocumentLoaderOptions,
|
|
15
|
+
getRemoteDocument,
|
|
16
|
+
type RemoteDocument,
|
|
17
|
+
} from "./docloader.ts";
|
|
18
|
+
export {
|
|
19
|
+
exportMultibaseKey,
|
|
20
|
+
exportSpki,
|
|
21
|
+
importMultibaseKey,
|
|
22
|
+
importPem,
|
|
23
|
+
importPkcs1,
|
|
24
|
+
importSpki,
|
|
25
|
+
} from "./key.ts";
|
|
26
|
+
export { LanguageString } from "./langstr.ts";
|
|
27
|
+
export {
|
|
28
|
+
decodeMultibase,
|
|
29
|
+
encodeMultibase,
|
|
30
|
+
encodingFromBaseData,
|
|
31
|
+
} from "./multibase/mod.ts";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { encodeText } from "./util.ts";
|
|
2
|
+
import type { BaseCode, BaseName, Codec, CodecFactory } from "./types.d.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Class to encode/decode in the supported Bases
|
|
6
|
+
*/
|
|
7
|
+
export class Base {
|
|
8
|
+
public codeBuf: Uint8Array;
|
|
9
|
+
codec: Codec;
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
public name: BaseName,
|
|
13
|
+
private code: BaseCode,
|
|
14
|
+
factory: CodecFactory,
|
|
15
|
+
private alphabet: string,
|
|
16
|
+
) {
|
|
17
|
+
this.codeBuf = encodeText(this.code);
|
|
18
|
+
this.alphabet = alphabet;
|
|
19
|
+
this.codec = factory(alphabet);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
encode(buf: Uint8Array): string {
|
|
23
|
+
return this.codec.encode(buf);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
decode(string: string): Uint8Array {
|
|
27
|
+
for (const char of string) {
|
|
28
|
+
if (this.alphabet && this.alphabet.indexOf(char) < 0) {
|
|
29
|
+
throw new Error(`invalid character '${char}' in '${string}'`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return this.codec.decode(string);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import baseX from "@multiformats/base-x";
|
|
2
|
+
import { Base } from "./base.ts";
|
|
3
|
+
import { rfc4648 } from "./rfc4648.ts";
|
|
4
|
+
import { decodeText, encodeText } from "./util.ts";
|
|
5
|
+
import type { BaseCode, BaseName, CodecFactory } from "./types.d.ts";
|
|
6
|
+
|
|
7
|
+
const identity: CodecFactory = () => {
|
|
8
|
+
return {
|
|
9
|
+
encode: decodeText,
|
|
10
|
+
decode: encodeText,
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* name, code, implementation, alphabet
|
|
16
|
+
*
|
|
17
|
+
* @type {Array<[BaseName, BaseCode, CodecFactory, string]>}
|
|
18
|
+
*/
|
|
19
|
+
const constants: Array<[BaseName, BaseCode, CodecFactory, string]> = [
|
|
20
|
+
["identity", "\x00", identity, ""],
|
|
21
|
+
["base2", "0", rfc4648(1), "01"],
|
|
22
|
+
["base8", "7", rfc4648(3), "01234567"],
|
|
23
|
+
["base10", "9", baseX, "0123456789"],
|
|
24
|
+
["base16", "f", rfc4648(4), "0123456789abcdef"],
|
|
25
|
+
["base16upper", "F", rfc4648(4), "0123456789ABCDEF"],
|
|
26
|
+
["base32hex", "v", rfc4648(5), "0123456789abcdefghijklmnopqrstuv"],
|
|
27
|
+
["base32hexupper", "V", rfc4648(5), "0123456789ABCDEFGHIJKLMNOPQRSTUV"],
|
|
28
|
+
["base32hexpad", "t", rfc4648(5), "0123456789abcdefghijklmnopqrstuv="],
|
|
29
|
+
["base32hexpadupper", "T", rfc4648(5), "0123456789ABCDEFGHIJKLMNOPQRSTUV="],
|
|
30
|
+
["base32", "b", rfc4648(5), "abcdefghijklmnopqrstuvwxyz234567"],
|
|
31
|
+
["base32upper", "B", rfc4648(5), "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"],
|
|
32
|
+
["base32pad", "c", rfc4648(5), "abcdefghijklmnopqrstuvwxyz234567="],
|
|
33
|
+
["base32padupper", "C", rfc4648(5), "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="],
|
|
34
|
+
["base32z", "h", rfc4648(5), "ybndrfg8ejkmcpqxot1uwisza345h769"],
|
|
35
|
+
["base36", "k", baseX, "0123456789abcdefghijklmnopqrstuvwxyz"],
|
|
36
|
+
["base36upper", "K", baseX, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"],
|
|
37
|
+
[
|
|
38
|
+
"base58btc",
|
|
39
|
+
"z",
|
|
40
|
+
baseX,
|
|
41
|
+
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
|
42
|
+
],
|
|
43
|
+
[
|
|
44
|
+
"base58flickr",
|
|
45
|
+
"Z",
|
|
46
|
+
baseX,
|
|
47
|
+
"123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ",
|
|
48
|
+
],
|
|
49
|
+
[
|
|
50
|
+
"base64",
|
|
51
|
+
"m",
|
|
52
|
+
rfc4648(6),
|
|
53
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
|
|
54
|
+
],
|
|
55
|
+
[
|
|
56
|
+
"base64pad",
|
|
57
|
+
"M",
|
|
58
|
+
rfc4648(6),
|
|
59
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
"base64url",
|
|
63
|
+
"u",
|
|
64
|
+
rfc4648(6),
|
|
65
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
"base64urlpad",
|
|
69
|
+
"U",
|
|
70
|
+
rfc4648(6),
|
|
71
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=",
|
|
72
|
+
],
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
export const names = constants.reduce<Record<BaseName, Base>>(
|
|
76
|
+
(prev, tupple) => {
|
|
77
|
+
prev[tupple[0]] = new Base(tupple[0], tupple[1], tupple[2], tupple[3]);
|
|
78
|
+
return prev;
|
|
79
|
+
},
|
|
80
|
+
{} as Record<BaseName, Base>,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
export const codes = constants.reduce<Record<BaseCode, Base>>(
|
|
84
|
+
(prev, tupple) => {
|
|
85
|
+
prev[tupple[1]] = names[tupple[0]];
|
|
86
|
+
return prev;
|
|
87
|
+
},
|
|
88
|
+
{} as Record<BaseCode, Base>,
|
|
89
|
+
);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Base } from "./base.ts";
|
|
2
|
+
import * as constants from "./constants.ts";
|
|
3
|
+
import type { BaseCode, BaseName, BaseNameOrCode } from "./types.d.ts";
|
|
4
|
+
import { concat, decodeText, encodeText } from "./util.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Encode data with the specified base and add the multibase prefix.
|
|
8
|
+
*
|
|
9
|
+
* @throws {Error} Will throw if the encoding is not supported
|
|
10
|
+
*/
|
|
11
|
+
export function encodeMultibase(
|
|
12
|
+
nameOrCode: BaseNameOrCode,
|
|
13
|
+
buf: Uint8Array,
|
|
14
|
+
): Uint8Array {
|
|
15
|
+
const enc = encoding(nameOrCode);
|
|
16
|
+
const data = encodeText(enc.encode(buf));
|
|
17
|
+
|
|
18
|
+
return concat([enc.codeBuf, data], enc.codeBuf.length + data.length);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Takes a Uint8Array or string encoded with multibase header, decodes it and
|
|
23
|
+
* returns the decoded buffer
|
|
24
|
+
*
|
|
25
|
+
* @throws {Error} Will throw if the encoding is not supported
|
|
26
|
+
*/
|
|
27
|
+
export function decodeMultibase(data: Uint8Array | string): Uint8Array {
|
|
28
|
+
if (data instanceof Uint8Array) {
|
|
29
|
+
data = decodeText(data);
|
|
30
|
+
}
|
|
31
|
+
const prefix = data[0];
|
|
32
|
+
|
|
33
|
+
// Make all encodings case-insensitive except the ones that include upper and lower chars in the alphabet
|
|
34
|
+
if (
|
|
35
|
+
["f", "F", "v", "V", "t", "T", "b", "B", "c", "C", "h", "k", "K"].includes(
|
|
36
|
+
prefix,
|
|
37
|
+
)
|
|
38
|
+
) {
|
|
39
|
+
data = data.toLowerCase();
|
|
40
|
+
}
|
|
41
|
+
const enc = encoding(data[0] as BaseCode);
|
|
42
|
+
return enc.decode(data.substring(1));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the encoding by name or code
|
|
47
|
+
* @throws {Error} Will throw if the encoding is not supported
|
|
48
|
+
*/
|
|
49
|
+
function encoding(nameOrCode: BaseNameOrCode): Base {
|
|
50
|
+
if (
|
|
51
|
+
Object.prototype.hasOwnProperty.call(
|
|
52
|
+
constants.names,
|
|
53
|
+
nameOrCode as BaseName,
|
|
54
|
+
)
|
|
55
|
+
) {
|
|
56
|
+
return constants.names[nameOrCode as BaseName];
|
|
57
|
+
} else if (
|
|
58
|
+
Object.prototype.hasOwnProperty.call(
|
|
59
|
+
constants.codes,
|
|
60
|
+
/** @type {BaseCode} */ (nameOrCode),
|
|
61
|
+
)
|
|
62
|
+
) {
|
|
63
|
+
return constants.codes[nameOrCode as BaseCode];
|
|
64
|
+
} else {
|
|
65
|
+
throw new Error(`Unsupported encoding: ${nameOrCode}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get encoding multibase from data
|
|
71
|
+
*
|
|
72
|
+
* @param {string|Uint8Array} data
|
|
73
|
+
* @returns {Base}
|
|
74
|
+
* @throws {Error} Will throw if the encoding is not supported
|
|
75
|
+
*/
|
|
76
|
+
export function encodingFromBaseData(data: string | Uint8Array): Base {
|
|
77
|
+
if (data instanceof Uint8Array) {
|
|
78
|
+
data = decodeText(data);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return encoding(data[0] as BaseCode);
|
|
82
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { deepStrictEqual } from "node:assert";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import * as constants from "./constants.ts";
|
|
4
|
+
import { decodeMultibase, encodeMultibase } from "./mod.ts";
|
|
5
|
+
import type { BaseName } from "./types.d.ts";
|
|
6
|
+
import { decodeText, encodeText } from "./util.ts";
|
|
7
|
+
|
|
8
|
+
test("multibase.encode and decode", async (t) => {
|
|
9
|
+
const testCases: Array<[BaseName, string, string]> = [
|
|
10
|
+
["base16", decodeText(Uint8Array.from([0x01])), "f01"],
|
|
11
|
+
["base16", decodeText(Uint8Array.from([15])), "f0f"],
|
|
12
|
+
["base16", "f", "f66"],
|
|
13
|
+
["base16", "fo", "f666f"],
|
|
14
|
+
["base16", "foo", "f666f6f"],
|
|
15
|
+
["base16", "foob", "f666f6f62"],
|
|
16
|
+
["base16", "fooba", "f666f6f6261"],
|
|
17
|
+
["base16", "foobar", "f666f6f626172"],
|
|
18
|
+
["base32", "yes mani !", "bpfsxgidnmfxgsibb"],
|
|
19
|
+
["base32", "f", "bmy"],
|
|
20
|
+
["base32", "fo", "bmzxq"],
|
|
21
|
+
["base32", "foo", "bmzxw6"],
|
|
22
|
+
["base32", "foob", "bmzxw6yq"],
|
|
23
|
+
["base32", "fooba", "bmzxw6ytb"],
|
|
24
|
+
["base32", "foobar", "bmzxw6ytboi"],
|
|
25
|
+
["base32pad", "yes mani !", "cpfsxgidnmfxgsibb"],
|
|
26
|
+
["base32pad", "f", "cmy======"],
|
|
27
|
+
["base32pad", "fo", "cmzxq===="],
|
|
28
|
+
["base32pad", "foo", "cmzxw6==="],
|
|
29
|
+
["base32pad", "foob", "cmzxw6yq="],
|
|
30
|
+
["base32pad", "fooba", "cmzxw6ytb"],
|
|
31
|
+
["base32pad", "foobar", "cmzxw6ytboi======"],
|
|
32
|
+
["base32hex", "yes mani !", "vf5in683dc5n6i811"],
|
|
33
|
+
["base32hex", "f", "vco"],
|
|
34
|
+
["base32hex", "fo", "vcpng"],
|
|
35
|
+
["base32hex", "foo", "vcpnmu"],
|
|
36
|
+
["base32hex", "foob", "vcpnmuog"],
|
|
37
|
+
["base32hex", "fooba", "vcpnmuoj1"],
|
|
38
|
+
["base32hex", "foobar", "vcpnmuoj1e8"],
|
|
39
|
+
["base32hexpad", "yes mani !", "tf5in683dc5n6i811"],
|
|
40
|
+
["base32hexpad", "f", "tco======"],
|
|
41
|
+
["base32hexpad", "fo", "tcpng===="],
|
|
42
|
+
["base32hexpad", "foo", "tcpnmu==="],
|
|
43
|
+
["base32hexpad", "foob", "tcpnmuog="],
|
|
44
|
+
["base32hexpad", "fooba", "tcpnmuoj1"],
|
|
45
|
+
["base32hexpad", "foobar", "tcpnmuoj1e8======"],
|
|
46
|
+
["base32z", "yes mani !", "hxf1zgedpcfzg1ebb"],
|
|
47
|
+
["base58flickr", "yes mani !", "Z7Pznk19XTTzBtx"],
|
|
48
|
+
["base58btc", "yes mani !", "z7paNL19xttacUY"],
|
|
49
|
+
["base64", "÷ïÿ", "mw7fDr8O/"],
|
|
50
|
+
["base64", "f", "mZg"],
|
|
51
|
+
["base64", "fo", "mZm8"],
|
|
52
|
+
["base64", "foo", "mZm9v"],
|
|
53
|
+
["base64", "foob", "mZm9vYg"],
|
|
54
|
+
["base64", "fooba", "mZm9vYmE"],
|
|
55
|
+
["base64", "foobar", "mZm9vYmFy"],
|
|
56
|
+
["base64", "÷ïÿ🥰÷ïÿ😎🥶🤯", "mw7fDr8O/8J+lsMO3w6/Dv/CfmI7wn6W28J+krw"],
|
|
57
|
+
["base64pad", "f", "MZg=="],
|
|
58
|
+
["base64pad", "fo", "MZm8="],
|
|
59
|
+
["base64pad", "foo", "MZm9v"],
|
|
60
|
+
["base64pad", "foob", "MZm9vYg=="],
|
|
61
|
+
["base64pad", "fooba", "MZm9vYmE="],
|
|
62
|
+
["base64pad", "foobar", "MZm9vYmFy"],
|
|
63
|
+
["base64url", "÷ïÿ", "uw7fDr8O_"],
|
|
64
|
+
["base64url", "÷ïÿ🥰÷ïÿ😎🥶🤯", "uw7fDr8O_8J-lsMO3w6_Dv_CfmI7wn6W28J-krw"],
|
|
65
|
+
["base64urlpad", "f", "UZg=="],
|
|
66
|
+
["base64urlpad", "fo", "UZm8="],
|
|
67
|
+
["base64urlpad", "foo", "UZm9v"],
|
|
68
|
+
["base64urlpad", "foob", "UZm9vYg=="],
|
|
69
|
+
["base64urlpad", "fooba", "UZm9vYmE="],
|
|
70
|
+
["base64urlpad", "foobar", "UZm9vYmFy"],
|
|
71
|
+
[
|
|
72
|
+
"base64urlpad",
|
|
73
|
+
"÷ïÿ🥰÷ïÿ😎🥶🤯",
|
|
74
|
+
"Uw7fDr8O_8J-lsMO3w6_Dv_CfmI7wn6W28J-krw==",
|
|
75
|
+
],
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
for (const [name, input, expectedOutput] of testCases) {
|
|
79
|
+
await t.test(`Encoding/Decoding ${name} with ${input}`, () => {
|
|
80
|
+
const encoded = encodeMultibase(name, encodeText(input));
|
|
81
|
+
deepStrictEqual(
|
|
82
|
+
decodeText(encoded),
|
|
83
|
+
expectedOutput,
|
|
84
|
+
`Encoding ${name} failed`,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const decoded = decodeMultibase(expectedOutput);
|
|
88
|
+
deepStrictEqual(decoded, encodeText(input), `Decoding ${name} failed`);
|
|
89
|
+
|
|
90
|
+
const decodedFromBuffer = decodeMultibase(encodeText(expectedOutput));
|
|
91
|
+
deepStrictEqual(
|
|
92
|
+
decodedFromBuffer,
|
|
93
|
+
encodeText(input),
|
|
94
|
+
`Decoding buffer of ${name} failed`,
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await t.test("should allow base32pad full alphabet", () => {
|
|
100
|
+
const encodedStr = "ctimaq4ygg2iegci7";
|
|
101
|
+
const decoded = decodeMultibase(encodedStr);
|
|
102
|
+
const encoded = encodeMultibase("c", decoded);
|
|
103
|
+
deepStrictEqual(decodeMultibase(encoded), decoded);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("constants", async (t) => {
|
|
108
|
+
await t.test("constants indexed by name", () => {
|
|
109
|
+
const names = constants.names;
|
|
110
|
+
deepStrictEqual(Object.keys(names).length, 23);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
await t.test("constants indexed by code", () => {
|
|
114
|
+
const codes = constants.codes;
|
|
115
|
+
deepStrictEqual(Object.keys(codes).length, 23);
|
|
116
|
+
});
|
|
117
|
+
});
|