@fedify/vocab-runtime 2.4.0-dev.2228 → 2.4.0-dev.2240
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/deno.json +2 -2
- package/dist/internal/jsonld-cache.cjs +1 -1
- package/dist/internal/jsonld-cache.js +1 -1
- package/dist/internal/portable-dereference.cjs +6 -18
- package/dist/internal/portable-dereference.js +4 -16
- package/dist/mod.cjs +5 -2
- package/dist/mod.d.cts +107 -4
- package/dist/mod.d.ts +107 -4
- package/dist/mod.js +3 -3
- package/dist/tests/{body-BmNV5g5S.mjs → body-C00dWiYW.mjs} +1 -1
- package/dist/tests/{body-BQZL1aJ1.cjs → body-DHk-vT0I.cjs} +1 -1
- package/dist/tests/body.test.cjs +1 -1
- package/dist/tests/body.test.mjs +1 -1
- package/dist/tests/decimal.test.cjs +3 -3
- package/dist/tests/decimal.test.mjs +3 -3
- package/dist/tests/{docloader-ButlYQxr.mjs → docloader-Bi982uOa.mjs} +3 -3
- package/dist/tests/{docloader-BnuGCXLg.cjs → docloader-gX-8knd3.cjs} +3 -3
- package/dist/tests/docloader.test.cjs +59 -65
- package/dist/tests/docloader.test.mjs +38 -44
- package/dist/tests/internal/portable-dereference.test.cjs +30 -157
- package/dist/tests/internal/portable-dereference.test.mjs +13 -140
- package/dist/tests/{jsonld-cache-C7mkqjiE.mjs → jsonld-cache-CKfgtxPB.mjs} +1 -1
- package/dist/tests/{jsonld-cache-CXA76Xi6.cjs → jsonld-cache-DzxT9IXH.cjs} +1 -1
- package/dist/tests/jsonld-cache.test.cjs +2 -2
- package/dist/tests/jsonld-cache.test.mjs +2 -2
- package/dist/tests/multibase/multibase.test.cjs +7 -7
- package/dist/tests/multibase/multibase.test.mjs +5 -5
- package/dist/tests/portable-dereference-C_cwGjz6.mjs +132 -0
- package/dist/tests/portable-dereference-CyFYTBnr.cjs +149 -0
- package/dist/tests/{portable-media-CFrGpz-3.cjs → portable-media-B39cvJGK.cjs} +3 -3
- package/dist/tests/{portable-media-xFDDMte7.mjs → portable-media-l0ET9Pq8.mjs} +3 -3
- package/dist/tests/portable-media.test.cjs +1 -1
- package/dist/tests/portable-media.test.mjs +1 -1
- package/dist/tests/portable-workers.test.cjs +36 -0
- package/dist/tests/portable-workers.test.d.cts +2 -0
- package/dist/tests/portable-workers.test.d.mts +2 -0
- package/dist/tests/portable-workers.test.mjs +35 -0
- package/dist/tests/{request-2GDEEJng.mjs → request-BRPiDcmG.mjs} +1 -1
- package/dist/tests/{request-Cme6ShRu.cjs → request-fo2l2yJS.cjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/dist/tests/{url-R9TTZ67B.mjs → url-CWC-NI3a.mjs} +224 -8
- package/dist/tests/{url-B6WlNhra.cjs → url-DsMBGqCc.cjs} +253 -7
- package/dist/tests/url.test.cjs +192 -1
- package/dist/tests/url.test.mjs +192 -1
- package/dist/{url-CHAbe3hE.cjs → url-BJMXv2kE.cjs} +253 -7
- package/dist/{url-BfguNa6K.js → url-CZm2-7ZO.js} +224 -8
- package/package.json +6 -3
- package/scripts/test-bun.mjs +17 -0
- package/src/docloader.test.ts +82 -74
- package/src/internal/portable-dereference.test.ts +14 -1
- package/src/internal/portable-dereference.ts +5 -19
- package/src/mod.ts +3 -0
- package/src/multibase/multibase.test.ts +5 -5
- package/src/portable-workers.test.ts +81 -0
- package/src/url.test.ts +385 -0
- package/src/url.ts +347 -13
package/src/url.test.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
formatIri,
|
|
8
8
|
fromCompatibleEf61Id,
|
|
9
9
|
getFe34Origin,
|
|
10
|
+
getGatewayHints,
|
|
10
11
|
haveSameFe34Origin,
|
|
11
12
|
haveSameIriOrigin,
|
|
12
13
|
isGatewayUrl,
|
|
@@ -19,6 +20,8 @@ import {
|
|
|
19
20
|
UrlError,
|
|
20
21
|
validateLookupAddresses,
|
|
21
22
|
validatePublicUrl,
|
|
23
|
+
withGatewayHints,
|
|
24
|
+
withoutGatewayHints,
|
|
22
25
|
} from "./url.ts";
|
|
23
26
|
|
|
24
27
|
test("parseIri() accepts portable ActivityPub URI schemes", () => {
|
|
@@ -37,6 +40,35 @@ test("parseIri() accepts portable ActivityPub URI schemes", () => {
|
|
|
37
40
|
}
|
|
38
41
|
});
|
|
39
42
|
|
|
43
|
+
test("portable URI parsing refuses dot segments before URL normalization", () => {
|
|
44
|
+
const base = "ap://did:key:z6Mkabc";
|
|
45
|
+
for (const segment of [".", "..", "%2e", ".%2E", "%2e.", "%2E%2e"]) {
|
|
46
|
+
for (const path of [`/${segment}`, `/a/${segment}/b`, `/a/${segment}`]) {
|
|
47
|
+
const iri = base + path;
|
|
48
|
+
throws(() => parseIri(iri), TypeError, iri);
|
|
49
|
+
throws(() => parseJsonLdId(iri), TypeError, iri);
|
|
50
|
+
throws(() => formatIri(iri), TypeError, iri);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (
|
|
54
|
+
const iri of [
|
|
55
|
+
`${base}/a/.\t./b`,
|
|
56
|
+
`${base}/a/..\n/b`,
|
|
57
|
+
` ${base}/a/../b`,
|
|
58
|
+
`${base}/a/.. `,
|
|
59
|
+
]
|
|
60
|
+
) {
|
|
61
|
+
throws(() => parseIri(iri), TypeError, iri);
|
|
62
|
+
}
|
|
63
|
+
for (const path of ["/a/.../b", "/a/.x/b", "/a/x.y/b", "/a/%252e/b"]) {
|
|
64
|
+
deepStrictEqual(
|
|
65
|
+
formatIri(parseIri(base + path)),
|
|
66
|
+
`ap+ef61://did:key:z6Mkabc${path}`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
throws(() => parseIri("child", `${base}/a/../b`), TypeError);
|
|
70
|
+
});
|
|
71
|
+
|
|
40
72
|
test("parseIri() normalizes DID scheme and method casing", () => {
|
|
41
73
|
const cases = [
|
|
42
74
|
"ap://DID:key:z6Mkabc/actor",
|
|
@@ -109,6 +141,19 @@ test("parseIri() resolves relative IRIs against portable string bases", () => {
|
|
|
109
141
|
);
|
|
110
142
|
});
|
|
111
143
|
|
|
144
|
+
test("parseIri() rejects lossy compatible ID string bases", () => {
|
|
145
|
+
const base = "https://server.example/.well-known/apgateway/did:key:z6MkAlice";
|
|
146
|
+
for (const segment of [".", "..", "%2e", "%2e%2e"]) {
|
|
147
|
+
const rawBase = `${base}/a/${segment}/b`;
|
|
148
|
+
throws(() => parseIri("child", rawBase), TypeError, rawBase);
|
|
149
|
+
throws(() => parseJsonLdId("child", rawBase), TypeError, rawBase);
|
|
150
|
+
}
|
|
151
|
+
deepStrictEqual(
|
|
152
|
+
parseIri("child", "https://server.example/a/../b"),
|
|
153
|
+
new URL("https://server.example/child"),
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
|
|
112
157
|
test("parseIri() resolves relative IRIs against at:// string bases", () => {
|
|
113
158
|
deepStrictEqual(
|
|
114
159
|
parseIri("/record", "at://did:plc:example/collection/item"),
|
|
@@ -814,6 +859,54 @@ test("fromCompatibleEf61Id() rejects malformed compatible identifiers", () => {
|
|
|
814
859
|
}
|
|
815
860
|
});
|
|
816
861
|
|
|
862
|
+
test("fromCompatibleEf61Id() refuses raw paths changed by URL parsing", () => {
|
|
863
|
+
const base = "https://server.example/.well-known/apgateway/did:key:z6MkAlice";
|
|
864
|
+
for (
|
|
865
|
+
const path of [
|
|
866
|
+
"/a/../b",
|
|
867
|
+
"/a/%2e%2E/b",
|
|
868
|
+
"/a/.\t./b",
|
|
869
|
+
"/a/.. ",
|
|
870
|
+
]
|
|
871
|
+
) {
|
|
872
|
+
throws(() => fromCompatibleEf61Id(base + path), TypeError, path);
|
|
873
|
+
throws(() => parseIri(base + path), TypeError, path);
|
|
874
|
+
throws(() => formatIri(base + path), TypeError, path);
|
|
875
|
+
}
|
|
876
|
+
for (
|
|
877
|
+
const input of [
|
|
878
|
+
"https:/server.example/.well-known/apgateway/did:key:z6MkAlice/a/../b",
|
|
879
|
+
"https:server.example/.well-known/apgateway/did:key:z6MkAlice/a/../b",
|
|
880
|
+
"https:\\\\server.example\\.well-known\\apgateway\\did:key:z6MkAlice\\a\\..\\b",
|
|
881
|
+
` ${base}/../../other`,
|
|
882
|
+
]
|
|
883
|
+
) {
|
|
884
|
+
throws(() => parseIri(input), TypeError, input);
|
|
885
|
+
throws(() => formatIri(input), TypeError, input);
|
|
886
|
+
}
|
|
887
|
+
throws(
|
|
888
|
+
() => fromCompatibleEf61Id(` ${base}/actor`),
|
|
889
|
+
TypeError,
|
|
890
|
+
);
|
|
891
|
+
throws(
|
|
892
|
+
() =>
|
|
893
|
+
fromCompatibleEf61Id(
|
|
894
|
+
`https://server.example/foo/../.well-known/apgateway/did:key:z6MkAlice/actor`,
|
|
895
|
+
),
|
|
896
|
+
TypeError,
|
|
897
|
+
);
|
|
898
|
+
deepStrictEqual(
|
|
899
|
+
fromCompatibleEf61Id(`${base}/a/%252e/b`)?.pathname,
|
|
900
|
+
"/a/%252e/b",
|
|
901
|
+
);
|
|
902
|
+
deepStrictEqual(
|
|
903
|
+
fromCompatibleEf61Id(
|
|
904
|
+
"https://server.example/.WELL-KNOWN/apgateway/did:key:z6MkAlice/actor",
|
|
905
|
+
),
|
|
906
|
+
null,
|
|
907
|
+
);
|
|
908
|
+
});
|
|
909
|
+
|
|
817
910
|
test("toCompatibleEf61Id() converts portable URIs", () => {
|
|
818
911
|
const cases: [string | URL, string | URL, string][] = [
|
|
819
912
|
[
|
|
@@ -1227,3 +1320,295 @@ test("expandIPv6Address()", () => {
|
|
|
1227
1320
|
"0064:ff9b:0000:0000:0000:0000:0808:0808",
|
|
1228
1321
|
);
|
|
1229
1322
|
});
|
|
1323
|
+
|
|
1324
|
+
test("withGatewayHints() adds @gateway location hints", () => {
|
|
1325
|
+
const expected = "ap+ef61://did:key:z6Mkabc/actor" +
|
|
1326
|
+
"?@gateway=https%3A%2F%2Fserver1.example" +
|
|
1327
|
+
"&@gateway=https%3A%2F%2Fserver2.example";
|
|
1328
|
+
for (
|
|
1329
|
+
const id of [
|
|
1330
|
+
"ap://did:key:z6Mkabc/actor",
|
|
1331
|
+
"ap+ef61://did:key:z6Mkabc/actor",
|
|
1332
|
+
"ap://did%3Akey%3Az6Mkabc/actor",
|
|
1333
|
+
parseIri("ap://did:key:z6Mkabc/actor"),
|
|
1334
|
+
new URL("ap://did%3Akey%3Az6Mkabc/actor"),
|
|
1335
|
+
]
|
|
1336
|
+
) {
|
|
1337
|
+
const hinted = withGatewayHints(id, [
|
|
1338
|
+
"https://server1.example",
|
|
1339
|
+
new URL("https://server2.example/"),
|
|
1340
|
+
]);
|
|
1341
|
+
ok(hinted instanceof URL);
|
|
1342
|
+
strictEqual(
|
|
1343
|
+
hinted.href,
|
|
1344
|
+
"ap+ef61://did%3Akey%3Az6Mkabc/actor" +
|
|
1345
|
+
"?@gateway=https%3A%2F%2Fserver1.example" +
|
|
1346
|
+
"&@gateway=https%3A%2F%2Fserver2.example",
|
|
1347
|
+
);
|
|
1348
|
+
strictEqual(formatIri(hinted), expected);
|
|
1349
|
+
deepStrictEqual(parseIri(formatIri(hinted)), hinted);
|
|
1350
|
+
}
|
|
1351
|
+
});
|
|
1352
|
+
|
|
1353
|
+
test("withGatewayHints() encodes and deduplicates gateway origins", () => {
|
|
1354
|
+
function* gateways(): Generator<string | URL> {
|
|
1355
|
+
yield "https://A.example:443";
|
|
1356
|
+
yield "https://a.example/";
|
|
1357
|
+
yield new URL("https://a.example");
|
|
1358
|
+
yield "https://b.example:8443";
|
|
1359
|
+
yield "http://c.example:80/";
|
|
1360
|
+
yield "https://例え.jp";
|
|
1361
|
+
}
|
|
1362
|
+
strictEqual(
|
|
1363
|
+
formatIri(withGatewayHints("ap://did:key:z6Mkabc/actor", gateways())),
|
|
1364
|
+
"ap+ef61://did:key:z6Mkabc/actor" +
|
|
1365
|
+
"?@gateway=https%3A%2F%2Fa.example" +
|
|
1366
|
+
"&@gateway=https%3A%2F%2Fb.example%3A8443" +
|
|
1367
|
+
"&@gateway=http%3A%2F%2Fc.example" +
|
|
1368
|
+
"&@gateway=https%3A%2F%2Fxn--r8jz45g.jp",
|
|
1369
|
+
);
|
|
1370
|
+
strictEqual(
|
|
1371
|
+
formatIri(
|
|
1372
|
+
withGatewayHints(
|
|
1373
|
+
"ap://did:key:z6Mkabc/actor",
|
|
1374
|
+
new Set(["https://b.example", "https://a.example"]),
|
|
1375
|
+
),
|
|
1376
|
+
),
|
|
1377
|
+
"ap+ef61://did:key:z6Mkabc/actor" +
|
|
1378
|
+
"?@gateway=https%3A%2F%2Fb.example&@gateway=https%3A%2F%2Fa.example",
|
|
1379
|
+
);
|
|
1380
|
+
// There is no limit on the number of hints:
|
|
1381
|
+
const many = Array.from(
|
|
1382
|
+
{ length: 7 },
|
|
1383
|
+
(_, i) => `https://server${i}.example`,
|
|
1384
|
+
);
|
|
1385
|
+
deepStrictEqual(
|
|
1386
|
+
getGatewayHints(withGatewayHints("ap://did:key:z6Mkabc/actor", many))
|
|
1387
|
+
.map((url) => url.origin),
|
|
1388
|
+
many,
|
|
1389
|
+
);
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
test("withGatewayHints() keeps other query parameters and fragments", () => {
|
|
1393
|
+
strictEqual(
|
|
1394
|
+
formatIri(
|
|
1395
|
+
withGatewayHints(
|
|
1396
|
+
"ap://did:key:z6Mkabc/collection?page=3&maxItems=20&q=a+b%20c%2b",
|
|
1397
|
+
["https://server.example"],
|
|
1398
|
+
),
|
|
1399
|
+
),
|
|
1400
|
+
"ap+ef61://did:key:z6Mkabc/collection?page=3&maxItems=20&q=a+b%20c%2B" +
|
|
1401
|
+
"&@gateway=https%3A%2F%2Fserver.example",
|
|
1402
|
+
);
|
|
1403
|
+
strictEqual(
|
|
1404
|
+
formatIri(
|
|
1405
|
+
withGatewayHints(
|
|
1406
|
+
"ap://did:key:z6Mkabc/actor?x=%26%3D#main-key",
|
|
1407
|
+
["https://server.example"],
|
|
1408
|
+
),
|
|
1409
|
+
),
|
|
1410
|
+
"ap+ef61://did:key:z6Mkabc/actor?x=%26%3D" +
|
|
1411
|
+
"&@gateway=https%3A%2F%2Fserver.example#main-key",
|
|
1412
|
+
);
|
|
1413
|
+
strictEqual(
|
|
1414
|
+
withGatewayHints("ap://did:key:z6Mkabc/actor#", ["https://s.example"])
|
|
1415
|
+
.href,
|
|
1416
|
+
"ap+ef61://did%3Akey%3Az6Mkabc/actor" +
|
|
1417
|
+
"?@gateway=https%3A%2F%2Fs.example#",
|
|
1418
|
+
);
|
|
1419
|
+
});
|
|
1420
|
+
|
|
1421
|
+
test("withGatewayHints() replaces existing location hints", () => {
|
|
1422
|
+
strictEqual(
|
|
1423
|
+
formatIri(
|
|
1424
|
+
withGatewayHints(
|
|
1425
|
+
"ap://did:key:z6Mkabc/collection?@gateway=https%3A%2F%2Fold.example" +
|
|
1426
|
+
"&page=2&%40gateway=https%3A%2F%2Fold2.example&@gateway" +
|
|
1427
|
+
"&gateways=https%3A%2F%2Flegacy.example&%2540gateway=kept",
|
|
1428
|
+
["https://new.example"],
|
|
1429
|
+
),
|
|
1430
|
+
),
|
|
1431
|
+
"ap+ef61://did:key:z6Mkabc/collection?page=2&%2540gateway=kept" +
|
|
1432
|
+
"&@gateway=https%3A%2F%2Fnew.example",
|
|
1433
|
+
);
|
|
1434
|
+
// Characters that the URL parser strips cannot turn into a hint name:
|
|
1435
|
+
for (const char of ["\t", "\n", "\r"]) {
|
|
1436
|
+
const hinted = withGatewayHints(
|
|
1437
|
+
`ap://did:key:z6Mkabc/actor?@gate${char}way=https%3A%2F%2Fevil.example`,
|
|
1438
|
+
["https://new.example"],
|
|
1439
|
+
);
|
|
1440
|
+
deepStrictEqual(getGatewayHints(hinted).map((url) => url.href), [
|
|
1441
|
+
"https://new.example/",
|
|
1442
|
+
]);
|
|
1443
|
+
}
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
test("withGatewayHints() with no gateways removes location hints", () => {
|
|
1447
|
+
for (
|
|
1448
|
+
const [input, expected] of [
|
|
1449
|
+
[
|
|
1450
|
+
"ap://did:key:z6Mkabc/actor?@gateway=https%3A%2F%2Fa.example",
|
|
1451
|
+
"ap+ef61://did%3Akey%3Az6Mkabc/actor",
|
|
1452
|
+
],
|
|
1453
|
+
[
|
|
1454
|
+
"ap://did:key:z6Mkabc/actor?&@gateway=https%3A%2F%2Fa.example&&p=1&",
|
|
1455
|
+
"ap+ef61://did%3Akey%3Az6Mkabc/actor?p=1",
|
|
1456
|
+
],
|
|
1457
|
+
["ap://did:key:z6Mkabc/actor?", "ap+ef61://did%3Akey%3Az6Mkabc/actor"],
|
|
1458
|
+
[
|
|
1459
|
+
"ap://did:key:z6Mkabc/actor?gateways=https%3A%2F%2Fa.example#k",
|
|
1460
|
+
"ap+ef61://did%3Akey%3Az6Mkabc/actor#k",
|
|
1461
|
+
],
|
|
1462
|
+
]
|
|
1463
|
+
) {
|
|
1464
|
+
strictEqual(withGatewayHints(input, []).href, expected, input);
|
|
1465
|
+
strictEqual(withoutGatewayHints(input).href, expected, input);
|
|
1466
|
+
}
|
|
1467
|
+
const input = parseIri(
|
|
1468
|
+
"ap://did:key:z6Mkabc/actor?@gateway=https%3A%2F%2Fa.example",
|
|
1469
|
+
);
|
|
1470
|
+
const href = input.href;
|
|
1471
|
+
withoutGatewayHints(input);
|
|
1472
|
+
strictEqual(input.href, href);
|
|
1473
|
+
});
|
|
1474
|
+
|
|
1475
|
+
test("withGatewayHints() rejects non-portable IDs", () => {
|
|
1476
|
+
for (
|
|
1477
|
+
const id of [
|
|
1478
|
+
"https://server.example/.well-known/apgateway/did:key:z6Mkabc/actor",
|
|
1479
|
+
"https://example.com/actor",
|
|
1480
|
+
"did:key:z6Mkabc#z6Mkabc",
|
|
1481
|
+
"ap://did:key:z6Mkabc",
|
|
1482
|
+
"ap://example.com/actor",
|
|
1483
|
+
"ap://did:key:z6Mkabc/actor%zz",
|
|
1484
|
+
new URL("https://example.com/actor"),
|
|
1485
|
+
new URL("ap://did%3Akey%3Az6Mkabc:8080/actor"),
|
|
1486
|
+
new URL("ap://user@did%3Akey%3Az6Mkabc/actor"),
|
|
1487
|
+
]
|
|
1488
|
+
) {
|
|
1489
|
+
throws(
|
|
1490
|
+
() => withGatewayHints(id, ["https://server.example"]),
|
|
1491
|
+
TypeError,
|
|
1492
|
+
String(id),
|
|
1493
|
+
);
|
|
1494
|
+
throws(() => withoutGatewayHints(id), TypeError, String(id));
|
|
1495
|
+
throws(() => getGatewayHints(id), TypeError, String(id));
|
|
1496
|
+
}
|
|
1497
|
+
});
|
|
1498
|
+
|
|
1499
|
+
test("withGatewayHints() rejects paths that URLs cannot represent", () => {
|
|
1500
|
+
for (
|
|
1501
|
+
const id of [
|
|
1502
|
+
"ap://did:key:z6Mkabc/a/../actor",
|
|
1503
|
+
"ap://did:key:z6Mkabc/a/./actor",
|
|
1504
|
+
"ap://did:key:z6Mkabc/a/%2e%2e/actor",
|
|
1505
|
+
"ap://did:key:z6Mkabc/a/%2E/actor",
|
|
1506
|
+
]
|
|
1507
|
+
) {
|
|
1508
|
+
throws(
|
|
1509
|
+
() => withGatewayHints(id, ["https://server.example"]),
|
|
1510
|
+
TypeError,
|
|
1511
|
+
id,
|
|
1512
|
+
);
|
|
1513
|
+
throws(() => withoutGatewayHints(id), TypeError, id);
|
|
1514
|
+
}
|
|
1515
|
+
// Characters that the URL parser would strip are percent-encoded instead:
|
|
1516
|
+
strictEqual(
|
|
1517
|
+
withoutGatewayHints("ap://did:key:z6Mkabc/a\tb").href,
|
|
1518
|
+
"ap+ef61://did%3Akey%3Az6Mkabc/a%09b",
|
|
1519
|
+
);
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
test("withGatewayHints() rejects invalid gateways", () => {
|
|
1523
|
+
for (
|
|
1524
|
+
const gateway of [
|
|
1525
|
+
"https://server.example/path",
|
|
1526
|
+
"https://server.example/?",
|
|
1527
|
+
"https://server.example/#",
|
|
1528
|
+
"https://server.example/?q=1",
|
|
1529
|
+
"https://user:pass@server.example",
|
|
1530
|
+
"ftp://server.example",
|
|
1531
|
+
"ap://did:key:z6Mkabc/actor",
|
|
1532
|
+
"server.example",
|
|
1533
|
+
new URL("https://server.example/path"),
|
|
1534
|
+
]
|
|
1535
|
+
) {
|
|
1536
|
+
throws(
|
|
1537
|
+
() => withGatewayHints("ap://did:key:z6Mkabc/actor", [gateway]),
|
|
1538
|
+
TypeError,
|
|
1539
|
+
String(gateway),
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
throws(
|
|
1543
|
+
() =>
|
|
1544
|
+
withGatewayHints(
|
|
1545
|
+
"ap://did:key:z6Mkabc/actor",
|
|
1546
|
+
"https://server.example" as unknown as string[],
|
|
1547
|
+
),
|
|
1548
|
+
TypeError,
|
|
1549
|
+
);
|
|
1550
|
+
});
|
|
1551
|
+
|
|
1552
|
+
test("getGatewayHints() reads @gateway location hints", () => {
|
|
1553
|
+
deepStrictEqual(
|
|
1554
|
+
getGatewayHints(
|
|
1555
|
+
"ap://did:key:z6Mkabc/actor?@gateway=https%3A%2F%2Fa.example" +
|
|
1556
|
+
"&@gateway=invalid&%40gateway=https%3A%2F%2Fb.example%2F" +
|
|
1557
|
+
"&@gateway=https%3A%2F%2Fa.example%2F" +
|
|
1558
|
+
"&@gateway=https%3A%2F%2Fc.example%2Fpath" +
|
|
1559
|
+
"&gateways=https%3A%2F%2Flegacy.example&page=1#k",
|
|
1560
|
+
).map((url) => url.href),
|
|
1561
|
+
["https://a.example/", "https://b.example/"],
|
|
1562
|
+
);
|
|
1563
|
+
deepStrictEqual(getGatewayHints("ap://did:key:z6Mkabc/actor"), []);
|
|
1564
|
+
// A literal question mark is a part of the parameter name:
|
|
1565
|
+
const questioned =
|
|
1566
|
+
"ap://did:key:z6Mkabc/actor??@gateway=https%3A%2F%2Fa.example";
|
|
1567
|
+
deepStrictEqual(getGatewayHints(questioned), []);
|
|
1568
|
+
strictEqual(
|
|
1569
|
+
withoutGatewayHints(questioned).href,
|
|
1570
|
+
"ap+ef61://did%3Akey%3Az6Mkabc/actor??@gateway=https%3A%2F%2Fa.example",
|
|
1571
|
+
);
|
|
1572
|
+
deepStrictEqual(
|
|
1573
|
+
getGatewayHints(withGatewayHints(questioned, ["https://b.example"]))
|
|
1574
|
+
.map((url) => url.href),
|
|
1575
|
+
["https://b.example/"],
|
|
1576
|
+
);
|
|
1577
|
+
deepStrictEqual(
|
|
1578
|
+
getGatewayHints(
|
|
1579
|
+
"ap://did:key:z6Mkabc/actor?@gate%09way=https%3A%2F%2Fa.example",
|
|
1580
|
+
),
|
|
1581
|
+
[],
|
|
1582
|
+
);
|
|
1583
|
+
deepStrictEqual(
|
|
1584
|
+
getGatewayHints(
|
|
1585
|
+
"ap://did:key:z6Mkabc/actor?@gate\tway=https%3A%2F%2Fa.example",
|
|
1586
|
+
),
|
|
1587
|
+
[],
|
|
1588
|
+
);
|
|
1589
|
+
});
|
|
1590
|
+
|
|
1591
|
+
test("withGatewayHints() round-trips with other portable ID helpers", () => {
|
|
1592
|
+
const hinted = withGatewayHints("ap://did:key:z6Mkabc/objects/1#frag", [
|
|
1593
|
+
"https://server1.example",
|
|
1594
|
+
"https://server2.example",
|
|
1595
|
+
]);
|
|
1596
|
+
strictEqual(
|
|
1597
|
+
canonicalizePortableUri(formatIri(hinted)),
|
|
1598
|
+
"ap+ef61://did:key:z6Mkabc/objects/1#frag",
|
|
1599
|
+
);
|
|
1600
|
+
ok(
|
|
1601
|
+
arePortableUrisEqual(
|
|
1602
|
+
formatIri(hinted),
|
|
1603
|
+
"ap://did:key:z6Mkabc/objects/1#frag",
|
|
1604
|
+
),
|
|
1605
|
+
);
|
|
1606
|
+
strictEqual(
|
|
1607
|
+
toCompatibleEf61Id(hinted, "https://server1.example").href,
|
|
1608
|
+
"https://server1.example/.well-known/apgateway/did:key:z6Mkabc/objects/1#frag",
|
|
1609
|
+
);
|
|
1610
|
+
strictEqual(
|
|
1611
|
+
formatIri(withoutGatewayHints(hinted)),
|
|
1612
|
+
"ap+ef61://did:key:z6Mkabc/objects/1#frag",
|
|
1613
|
+
);
|
|
1614
|
+
});
|