@uncefact/untp-utils 0.2.0 → 0.3.0
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/README.md +40 -12
- package/artefacts/context/untp/0.6.0/dcc.json +1241 -0
- package/artefacts/context/untp/0.6.0/dfr.json +619 -0
- package/artefacts/context/untp/0.6.0/dia.json +162 -0
- package/artefacts/context/untp/0.6.0/dpp.json +1078 -0
- package/artefacts/context/untp/0.6.0/dte.json +1285 -0
- package/artefacts/context/untp/0.6.1/dcc.json +1388 -0
- package/artefacts/context/untp/0.6.1/dfr.json +690 -0
- package/artefacts/context/untp/0.6.1/dia.json +196 -0
- package/artefacts/context/untp/0.6.1/dpp.json +1164 -0
- package/artefacts/context/untp/0.6.1/dte.json +1340 -0
- package/artefacts/context/untp/0.7.0/untp.json +3493 -0
- package/artefacts/context/vcdm/2/credentials.json +301 -0
- package/artefacts/manifest.json +214 -0
- package/artefacts/schema/untp/0.6.0/dcc.json +1131 -0
- package/artefacts/schema/untp/0.6.0/dfr.json +1028 -0
- package/artefacts/schema/untp/0.6.0/dia.json +236 -0
- package/artefacts/schema/untp/0.6.0/dpp.json +1219 -0
- package/artefacts/schema/untp/0.6.0/dte.json +845 -0
- package/artefacts/schema/untp/0.6.1/dcc.json +1196 -0
- package/artefacts/schema/untp/0.6.1/dfr.json +1037 -0
- package/artefacts/schema/untp/0.6.1/dia.json +236 -0
- package/artefacts/schema/untp/0.6.1/dpp.json +1229 -0
- package/artefacts/schema/untp/0.6.1/dte.json +845 -0
- package/artefacts/schema/untp/0.7.0/cvc.json +701 -0
- package/artefacts/schema/untp/0.7.0/dcc.json +1133 -0
- package/artefacts/schema/untp/0.7.0/dfr.json +1245 -0
- package/artefacts/schema/untp/0.7.0/dia.json +443 -0
- package/artefacts/schema/untp/0.7.0/dpp.json +1300 -0
- package/artefacts/schema/untp/0.7.0/dte.json +1226 -0
- package/artefacts/schema/untp/0.7.0/linkset.json +119 -0
- package/artefacts/schema/vcdm/2/verifiable-credential.json +318 -0
- package/build/artefacts/urls.d.ts +6 -0
- package/build/artefacts/urls.js +8 -0
- package/build/artefacts/urls.js.map +1 -1
- package/build/artefacts/urls.test.js +9 -1
- package/build/artefacts/urls.test.js.map +1 -1
- package/build/bundle/artefacts/context/untp/0.6.0/dcc.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dcc.js +1244 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dcc.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dfr.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dfr.js +622 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dfr.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dia.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dia.js +165 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dia.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dpp.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dpp.js +1081 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dpp.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dte.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dte.js +1288 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dte.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dcc.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dcc.js +1391 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dcc.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dfr.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dfr.js +693 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dfr.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dia.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dia.js +199 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dia.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dpp.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dpp.js +1167 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dpp.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dte.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dte.js +1343 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dte.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.7.0/untp.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.7.0/untp.js +3496 -0
- package/build/bundle/artefacts/context/untp/0.7.0/untp.js.map +1 -0
- package/build/bundle/artefacts/context/vcdm/2/credentials.d.ts +2 -0
- package/build/bundle/artefacts/context/vcdm/2/credentials.js +304 -0
- package/build/bundle/artefacts/context/vcdm/2/credentials.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dcc.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dcc.js +1134 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dcc.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dfr.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dfr.js +1031 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dfr.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dia.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dia.js +239 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dia.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dpp.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dpp.js +1222 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dpp.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dte.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dte.js +848 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dte.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dcc.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dcc.js +1199 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dcc.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dfr.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dfr.js +1040 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dfr.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dia.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dia.js +239 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dia.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dpp.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dpp.js +1232 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dpp.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dte.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dte.js +848 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dte.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/cvc.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/cvc.js +704 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/cvc.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dcc.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dcc.js +1136 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dcc.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dfr.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dfr.js +1248 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dfr.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dia.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dia.js +446 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dia.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dpp.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dpp.js +1303 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dpp.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dte.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dte.js +1229 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dte.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/linkset.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/linkset.js +122 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/linkset.js.map +1 -0
- package/build/bundle/artefacts/schema/vcdm/2/verifiable-credential.d.ts +2 -0
- package/build/bundle/artefacts/schema/vcdm/2/verifiable-credential.js +321 -0
- package/build/bundle/artefacts/schema/vcdm/2/verifiable-credential.js.map +1 -0
- package/build/bundle/fallback.d.ts +41 -0
- package/build/bundle/fallback.js +62 -0
- package/build/bundle/fallback.js.map +1 -0
- package/build/bundle/fallback.test.d.ts +1 -0
- package/build/bundle/fallback.test.js +124 -0
- package/build/bundle/fallback.test.js.map +1 -0
- package/build/bundle/index.d.ts +4 -0
- package/build/bundle/index.js +77 -0
- package/build/bundle/index.js.map +1 -0
- package/build/bundle/lookup.d.ts +14 -0
- package/build/bundle/lookup.js +40 -0
- package/build/bundle/lookup.js.map +1 -0
- package/build/bundle/lookup.test.d.ts +1 -0
- package/build/bundle/lookup.test.js +70 -0
- package/build/bundle/lookup.test.js.map +1 -0
- package/build/bundle/public.d.ts +25 -0
- package/build/bundle/public.js +56 -0
- package/build/bundle/public.js.map +1 -0
- package/build/bundle/public.test.d.ts +1 -0
- package/build/bundle/public.test.js +35 -0
- package/build/bundle/public.test.js.map +1 -0
- package/build/bundle/refresh-classify.test.d.ts +1 -0
- package/build/bundle/refresh-classify.test.js +20 -0
- package/build/bundle/refresh-classify.test.js.map +1 -0
- package/build/bundle/refresh-main.test.d.ts +1 -0
- package/build/bundle/refresh-main.test.js +98 -0
- package/build/bundle/refresh-main.test.js.map +1 -0
- package/build/common/as-date-time.d.ts +13 -0
- package/build/common/as-date-time.js +44 -0
- package/build/common/as-date-time.js.map +1 -0
- package/build/common/as-date-time.test.d.ts +1 -0
- package/build/common/as-date-time.test.js +42 -0
- package/build/common/as-date-time.test.js.map +1 -0
- package/build/common/as-non-empty-string.d.ts +11 -4
- package/build/common/as-non-empty-string.js +15 -5
- package/build/common/as-non-empty-string.js.map +1 -1
- package/build/common/as-non-empty-string.test.js +7 -2
- package/build/common/as-non-empty-string.test.js.map +1 -1
- package/build/common/index.d.ts +4 -0
- package/build/common/index.js +5 -0
- package/build/common/index.js.map +1 -0
- package/build/common/validity-window.d.ts +45 -0
- package/build/common/validity-window.js +68 -0
- package/build/common/validity-window.js.map +1 -0
- package/build/common/validity-window.test.d.ts +1 -0
- package/build/common/validity-window.test.js +67 -0
- package/build/common/validity-window.test.js.map +1 -0
- package/build/loaders/index.d.ts +2 -0
- package/build/loaders/index.js +1 -0
- package/build/loaders/index.js.map +1 -1
- package/build/loaders/jsonld-document-loader.d.ts +7 -5
- package/build/loaders/jsonld-document-loader.js +11 -2
- package/build/loaders/jsonld-document-loader.js.map +1 -1
- package/build/loaders/jsonld-document-loader.test.js +41 -1
- package/build/loaders/jsonld-document-loader.test.js.map +1 -1
- package/build/loaders/schema-loader.d.ts +13 -3
- package/build/loaders/schema-loader.js +24 -8
- package/build/loaders/schema-loader.js.map +1 -1
- package/build/loaders/schema-loader.test.js +56 -1
- package/build/loaders/schema-loader.test.js.map +1 -1
- package/build/node/errors.d.ts +9 -3
- package/build/node/errors.js +9 -3
- package/build/node/errors.js.map +1 -1
- package/build/node/index.d.ts +1 -1
- package/build/node/index.js +1 -1
- package/build/node/index.js.map +1 -1
- package/build/node/is-private-ip.d.ts +8 -4
- package/build/node/is-private-ip.fail-closed.test.d.ts +1 -0
- package/build/node/is-private-ip.fail-closed.test.js +36 -0
- package/build/node/is-private-ip.fail-closed.test.js.map +1 -0
- package/build/node/is-private-ip.js +77 -12
- package/build/node/is-private-ip.js.map +1 -1
- package/build/node/is-private-ip.test.js +28 -0
- package/build/node/is-private-ip.test.js.map +1 -1
- package/build/node/validate-public-url.d.ts +43 -11
- package/build/node/validate-public-url.js +68 -34
- package/build/node/validate-public-url.js.map +1 -1
- package/build/node/validate-public-url.test.js +161 -14
- package/build/node/validate-public-url.test.js.map +1 -1
- package/build/resolvers/errors.d.ts +36 -6
- package/build/resolvers/errors.js +41 -7
- package/build/resolvers/errors.js.map +1 -1
- package/build/resolvers/resolve-document-if-changed.test.js +7 -0
- package/build/resolvers/resolve-document-if-changed.test.js.map +1 -1
- package/build/resolvers/resolve-document.d.ts +29 -9
- package/build/resolvers/resolve-document.integration.test.d.ts +1 -0
- package/build/resolvers/resolve-document.integration.test.js +108 -0
- package/build/resolvers/resolve-document.integration.test.js.map +1 -0
- package/build/resolvers/resolve-document.js +129 -49
- package/build/resolvers/resolve-document.js.map +1 -1
- package/build/resolvers/resolve-document.test.js +462 -12
- package/build/resolvers/resolve-document.test.js.map +1 -1
- package/build/resolvers/resolve-json-document.d.ts +11 -8
- package/build/resolvers/resolve-json-document.js +7 -6
- package/build/resolvers/resolve-json-document.js.map +1 -1
- package/build/resolvers/resolve-json-document.test.js +21 -4
- package/build/resolvers/resolve-json-document.test.js.map +1 -1
- package/build/validation/describe-jsonld-failure.d.ts +48 -8
- package/build/validation/describe-jsonld-failure.js +94 -27
- package/build/validation/describe-jsonld-failure.js.map +1 -1
- package/build/validation/describe-jsonld-failure.test.js +98 -13
- package/build/validation/describe-jsonld-failure.test.js.map +1 -1
- package/build/validation/index.d.ts +3 -2
- package/build/validation/index.js +2 -2
- package/build/validation/index.js.map +1 -1
- package/build/validation/validate-jsonld.d.ts +29 -3
- package/build/validation/validate-jsonld.js +49 -22
- package/build/validation/validate-jsonld.js.map +1 -1
- package/build/validation/validate-jsonld.test.js +62 -5
- package/build/validation/validate-jsonld.test.js.map +1 -1
- package/package.json +23 -3
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// Drives the refresh script's main() against a temporary copy of the bundle
|
|
2
|
+
// with fetch stubbed, so the refusal, repair and dry-run paths are proven
|
|
3
|
+
// without the network. The script is loaded by runtime URL (tsc's rootDir is
|
|
4
|
+
// `src`); UNTP_ARTEFACTS_ROOT points it at the temporary tree.
|
|
5
|
+
import { jest } from '@jest/globals';
|
|
6
|
+
import { cp, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
const scriptUrl = new URL('../../scripts/refresh-artefacts.mjs', import.meta.url).href;
|
|
10
|
+
const packageRoot = new URL('../../', import.meta.url).pathname;
|
|
11
|
+
describe('refresh-artefacts main', () => {
|
|
12
|
+
let root;
|
|
13
|
+
let script;
|
|
14
|
+
let exit;
|
|
15
|
+
let stubbed;
|
|
16
|
+
let fetchImpl;
|
|
17
|
+
const run = (argv) => script.main(argv, { fetchImpl });
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
// The script resolves its root when it loads, so the temporary root is
|
|
20
|
+
// fixed before the import and refilled from the real bundle per test.
|
|
21
|
+
root = await mkdtemp(join(tmpdir(), 'untp-artefacts-'));
|
|
22
|
+
process.env.UNTP_ARTEFACTS_ROOT = root;
|
|
23
|
+
script = (await import(scriptUrl));
|
|
24
|
+
});
|
|
25
|
+
afterAll(async () => {
|
|
26
|
+
delete process.env.UNTP_ARTEFACTS_ROOT;
|
|
27
|
+
await rm(root, { recursive: true, force: true });
|
|
28
|
+
});
|
|
29
|
+
beforeEach(async () => {
|
|
30
|
+
await rm(join(root, 'artefacts'), { recursive: true, force: true });
|
|
31
|
+
await rm(join(root, 'src'), { recursive: true, force: true });
|
|
32
|
+
await cp(join(packageRoot, 'artefacts'), join(root, 'artefacts'), { recursive: true });
|
|
33
|
+
await cp(join(packageRoot, 'src', 'bundle'), join(root, 'src', 'bundle'), { recursive: true });
|
|
34
|
+
// Serve every source from the bundled copy on disk, so "published" equals "bundled".
|
|
35
|
+
stubbed = new Map();
|
|
36
|
+
for (const { file, source } of script.ARTEFACTS) {
|
|
37
|
+
stubbed.set(source, await readFile(join(packageRoot, 'artefacts', file), 'utf8'));
|
|
38
|
+
}
|
|
39
|
+
fetchImpl = (async (url) => {
|
|
40
|
+
const body = stubbed.get(String(url));
|
|
41
|
+
return body === undefined
|
|
42
|
+
? new Response('missing', { status: 404 })
|
|
43
|
+
: new Response(body, { status: 200, headers: { 'content-type': 'application/json' } });
|
|
44
|
+
});
|
|
45
|
+
exit = jest.spyOn(process, 'exit').mockImplementation(((code) => {
|
|
46
|
+
throw new Error(`exit ${code}`);
|
|
47
|
+
}));
|
|
48
|
+
jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
49
|
+
jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
50
|
+
});
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
jest.restoreAllMocks();
|
|
53
|
+
});
|
|
54
|
+
const corruptFile = 'context/untp/0.6.0/dia.json';
|
|
55
|
+
it('--check passes on a matching tree and writes nothing', async () => {
|
|
56
|
+
const before = await readFile(join(root, 'artefacts', 'manifest.json'), 'utf8');
|
|
57
|
+
await run(['--check']);
|
|
58
|
+
expect(exit).not.toHaveBeenCalled();
|
|
59
|
+
expect(await readFile(join(root, 'artefacts', 'manifest.json'), 'utf8')).toBe(before);
|
|
60
|
+
});
|
|
61
|
+
it('--check refuses a corrupt bundled copy, naming it, and writes nothing', async () => {
|
|
62
|
+
await writeFile(join(root, 'artefacts', corruptFile), '{not json');
|
|
63
|
+
await expect(run(['--check'])).rejects.toThrow('exit 1');
|
|
64
|
+
expect(await readFile(join(root, 'artefacts', corruptFile), 'utf8')).toBe('{not json');
|
|
65
|
+
});
|
|
66
|
+
it('a plain refresh also refuses a corrupt copy rather than hashing it', async () => {
|
|
67
|
+
await writeFile(join(root, 'artefacts', corruptFile), '{not json');
|
|
68
|
+
await expect(run([])).rejects.toThrow('exit 1');
|
|
69
|
+
expect(await readFile(join(root, 'artefacts', corruptFile), 'utf8')).toBe('{not json');
|
|
70
|
+
});
|
|
71
|
+
it('--force repairs a corrupt copy from the published source', async () => {
|
|
72
|
+
await writeFile(join(root, 'artefacts', corruptFile), '{not json');
|
|
73
|
+
await run(['--force']);
|
|
74
|
+
expect(exit).not.toHaveBeenCalled();
|
|
75
|
+
expect(JSON.parse(await readFile(join(root, 'artefacts', corruptFile), 'utf8'))).toHaveProperty('@context');
|
|
76
|
+
});
|
|
77
|
+
it('refuses a published copy that differs unless forced', async () => {
|
|
78
|
+
const entry = script.ARTEFACTS.find((a) => a.file === corruptFile);
|
|
79
|
+
stubbed.set(entry.source, JSON.stringify({ '@context': { changed: true } }));
|
|
80
|
+
await expect(run(['--check'])).rejects.toThrow('exit 1');
|
|
81
|
+
await expect(run([])).rejects.toThrow('exit 1');
|
|
82
|
+
await run(['--force']);
|
|
83
|
+
expect(JSON.parse(await readFile(join(root, 'artefacts', corruptFile), 'utf8'))).toEqual({
|
|
84
|
+
'@context': { changed: true },
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
it('writes nothing when a later fetch fails', async () => {
|
|
88
|
+
const entry = script.ARTEFACTS[script.ARTEFACTS.length - 1];
|
|
89
|
+
stubbed.delete(entry.source);
|
|
90
|
+
await writeFile(join(root, 'artefacts', corruptFile), '{not json');
|
|
91
|
+
await expect(run(['--force'])).rejects.toThrow(/answered 404/);
|
|
92
|
+
expect(await readFile(join(root, 'artefacts', corruptFile), 'utf8')).toBe('{not json');
|
|
93
|
+
});
|
|
94
|
+
it('rejects --check together with --force', async () => {
|
|
95
|
+
await expect(run(['--check', '--force'])).rejects.toThrow('exit 1');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
//# sourceMappingURL=refresh-main.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh-main.test.js","sourceRoot":"","sources":["../../src/bundle/refresh-main.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the date and time `value` refers to, when `value` is a well-formed
|
|
3
|
+
* RFC 3339 date-time for a day that exists. Otherwise returns `undefined`.
|
|
4
|
+
*
|
|
5
|
+
* `new Date()` alone is not enough. It accepts loose forms such as `2024-01-15`
|
|
6
|
+
* or `January 15, 2024`, and it quietly normalises an impossible date, turning
|
|
7
|
+
* `2024-02-30` into 1 March, which would report a date the document never
|
|
8
|
+
* asserted.
|
|
9
|
+
*
|
|
10
|
+
* `undefined` rather than `null` because this reports what a document does not
|
|
11
|
+
* usably contain. How to record that absence is the caller's decision.
|
|
12
|
+
*/
|
|
13
|
+
export declare function asDateTime(value: unknown): Date | undefined;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matches the JSON Schema `date-time` format (RFC 3339 section 5.6).
|
|
3
|
+
*
|
|
4
|
+
* As of UNTP 0.7.0, every data model declares a credential's `validFrom` and
|
|
5
|
+
* `validUntil` as `"type": "string", "format": "date-time"`, and that is what
|
|
6
|
+
* this system validates a payload against before signing it.
|
|
7
|
+
*
|
|
8
|
+
* The W3C VC Data Model 2.0 is looser: it requires only an XML Schema 1.1
|
|
9
|
+
* `dateTime`, which permits an offsetless local time, years outside four
|
|
10
|
+
* digits, and `24:00:00` (https://www.w3.org/TR/vc-data-model-2.0/#validity-period).
|
|
11
|
+
* A credential issued elsewhere may therefore carry a value this rejects, and
|
|
12
|
+
* it is reported as absent rather than guessed: with no timezone there is no
|
|
13
|
+
* way to tell which moment a local time such as `09:00` refers to.
|
|
14
|
+
*/
|
|
15
|
+
const RFC3339_DATE_TIME = /^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(\.\d+)?([Zz]|[+-]\d{2}:\d{2})$/;
|
|
16
|
+
/**
|
|
17
|
+
* Returns the date and time `value` refers to, when `value` is a well-formed
|
|
18
|
+
* RFC 3339 date-time for a day that exists. Otherwise returns `undefined`.
|
|
19
|
+
*
|
|
20
|
+
* `new Date()` alone is not enough. It accepts loose forms such as `2024-01-15`
|
|
21
|
+
* or `January 15, 2024`, and it quietly normalises an impossible date, turning
|
|
22
|
+
* `2024-02-30` into 1 March, which would report a date the document never
|
|
23
|
+
* asserted.
|
|
24
|
+
*
|
|
25
|
+
* `undefined` rather than `null` because this reports what a document does not
|
|
26
|
+
* usably contain. How to record that absence is the caller's decision.
|
|
27
|
+
*/
|
|
28
|
+
export function asDateTime(value) {
|
|
29
|
+
if (typeof value !== 'string' || !RFC3339_DATE_TIME.test(value))
|
|
30
|
+
return undefined;
|
|
31
|
+
const parsed = new Date(value);
|
|
32
|
+
if (Number.isNaN(parsed.getTime()))
|
|
33
|
+
return undefined;
|
|
34
|
+
// Round-trip the calendar day: a normalised day (30 February) lands on a
|
|
35
|
+
// different day-of-month than the string names.
|
|
36
|
+
const day = Number(value.slice(8, 10));
|
|
37
|
+
const offsetMatch = /([+-])(\d{2}):(\d{2})$/.exec(value);
|
|
38
|
+
const offsetMinutes = offsetMatch
|
|
39
|
+
? (offsetMatch[1] === '-' ? -1 : 1) * (Number(offsetMatch[2]) * 60 + Number(offsetMatch[3]))
|
|
40
|
+
: 0;
|
|
41
|
+
const local = new Date(parsed.getTime() + offsetMinutes * 60_000);
|
|
42
|
+
return local.getUTCDate() === day ? parsed : undefined;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=as-date-time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"as-date-time.js","sourceRoot":"","sources":["../../src/common/as-date-time.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,iBAAiB,GAAG,wEAAwE,CAAC;AAEnG;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAClF,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IACrD,yEAAyE;IACzE,gDAAgD;IAChD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACvC,MAAM,WAAW,GAAG,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzD,MAAM,aAAa,GAAG,WAAW;QAC/B,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5F,CAAC,CAAC,CAAC,CAAC;IACN,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,aAAa,GAAG,MAAM,CAAC,CAAC;IAClE,OAAO,KAAK,CAAC,UAAU,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { asDateTime } from './as-date-time.js';
|
|
2
|
+
describe('asDateTime', () => {
|
|
3
|
+
it('returns the date and time a well-formed UTC value refers to', () => {
|
|
4
|
+
expect(asDateTime('2024-01-15T00:00:00Z')).toEqual(new Date('2024-01-15T00:00:00.000Z'));
|
|
5
|
+
expect(asDateTime('2024-01-15T10:30:00.250Z')).toEqual(new Date('2024-01-15T10:30:00.250Z'));
|
|
6
|
+
});
|
|
7
|
+
it('reads an offset value as the same moment in UTC', () => {
|
|
8
|
+
expect(asDateTime('2024-01-15T23:30:00+10:00')).toEqual(new Date('2024-01-15T13:30:00.000Z'));
|
|
9
|
+
expect(asDateTime('2024-01-15T00:30:00-05:00')).toEqual(new Date('2024-01-15T05:30:00.000Z'));
|
|
10
|
+
});
|
|
11
|
+
it('returns undefined for a day the calendar does not have, rather than normalising it', () => {
|
|
12
|
+
expect(asDateTime('2024-02-30T00:00:00Z')).toBeUndefined();
|
|
13
|
+
expect(asDateTime('2023-02-29T00:00:00Z')).toBeUndefined();
|
|
14
|
+
});
|
|
15
|
+
it('accepts a real leap day', () => {
|
|
16
|
+
expect(asDateTime('2024-02-29T00:00:00Z')).toEqual(new Date('2024-02-29T00:00:00.000Z'));
|
|
17
|
+
});
|
|
18
|
+
it('returns undefined for a leap second', () => {
|
|
19
|
+
// Well-formed RFC 3339, but JavaScript has no value for it.
|
|
20
|
+
expect(asDateTime('1990-12-31T23:59:60Z')).toBeUndefined();
|
|
21
|
+
});
|
|
22
|
+
it.each([
|
|
23
|
+
['a date with no time', '2024-01-15'],
|
|
24
|
+
['a human-readable date', 'January 15, 2024'],
|
|
25
|
+
['a bare number', '0'],
|
|
26
|
+
['nonsense', 'not-a-date'],
|
|
27
|
+
['the empty string', ''],
|
|
28
|
+
['a time with no zone', '2024-01-15T00:00:00'],
|
|
29
|
+
])('returns undefined for %s', (_label, value) => {
|
|
30
|
+
expect(asDateTime(value)).toBeUndefined();
|
|
31
|
+
});
|
|
32
|
+
it.each([
|
|
33
|
+
['number', 1705276800000],
|
|
34
|
+
['null', null],
|
|
35
|
+
['undefined', undefined],
|
|
36
|
+
['Date', new Date()],
|
|
37
|
+
['object', {}],
|
|
38
|
+
])('returns undefined for a non-string %s', (_label, value) => {
|
|
39
|
+
expect(asDateTime(value)).toBeUndefined();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=as-date-time.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"as-date-time.test.js","sourceRoot":"","sources":["../../src/common/as-date-time.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;QACzF,MAAM,CAAC,UAAU,CAAC,0BAA0B,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;IAC/F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,UAAU,CAAC,2BAA2B,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;QAC9F,MAAM,CAAC,UAAU,CAAC,2BAA2B,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oFAAoF,EAAE,GAAG,EAAE;QAC5F,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAC3D,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,4DAA4D;QAC5D,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC;QACN,CAAC,qBAAqB,EAAE,YAAY,CAAC;QACrC,CAAC,uBAAuB,EAAE,kBAAkB,CAAC;QAC7C,CAAC,eAAe,EAAE,GAAG,CAAC;QACtB,CAAC,UAAU,EAAE,YAAY,CAAC;QAC1B,CAAC,kBAAkB,EAAE,EAAE,CAAC;QACxB,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;KAC/C,CAAC,CAAC,0BAA0B,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QAC/C,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC;QACN,CAAC,QAAQ,EAAE,aAAa,CAAC;QACzB,CAAC,MAAM,EAAE,IAAI,CAAC;QACd,CAAC,WAAW,EAAE,SAAS,CAAC;QACxB,CAAC,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;QACpB,CAAC,QAAQ,EAAE,EAAE,CAAC;KACf,CAAC,CAAC,uCAAuC,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QAC5D,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Returns `value` if it is a non-
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Returns `value` trimmed if it is a string with non-whitespace content,
|
|
3
|
+
* otherwise `undefined`.
|
|
4
|
+
*
|
|
5
|
+
* Surrounding whitespace is never meaningful in the values this reads (names,
|
|
6
|
+
* identifiers, labels), and a whitespace-only string carries no more
|
|
7
|
+
* information than an absent one, so both collapse to `undefined`.
|
|
8
|
+
*
|
|
9
|
+
* `undefined` rather than `null` because this reports what a document does
|
|
10
|
+
* not contain. Recording that absence as a stored or transmitted value is a
|
|
11
|
+
* separate decision, made by the caller at the boundary where a read becomes
|
|
12
|
+
* a row or a response.
|
|
6
13
|
*/
|
|
7
14
|
export declare function asNonEmptyString(value: unknown): string | undefined;
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Returns `value` if it is a non-
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Returns `value` trimmed if it is a string with non-whitespace content,
|
|
3
|
+
* otherwise `undefined`.
|
|
4
|
+
*
|
|
5
|
+
* Surrounding whitespace is never meaningful in the values this reads (names,
|
|
6
|
+
* identifiers, labels), and a whitespace-only string carries no more
|
|
7
|
+
* information than an absent one, so both collapse to `undefined`.
|
|
8
|
+
*
|
|
9
|
+
* `undefined` rather than `null` because this reports what a document does
|
|
10
|
+
* not contain. Recording that absence as a stored or transmitted value is a
|
|
11
|
+
* separate decision, made by the caller at the boundary where a read becomes
|
|
12
|
+
* a row or a response.
|
|
6
13
|
*/
|
|
7
14
|
export function asNonEmptyString(value) {
|
|
8
|
-
|
|
15
|
+
if (typeof value !== 'string')
|
|
16
|
+
return undefined;
|
|
17
|
+
const trimmed = value.trim();
|
|
18
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
9
19
|
}
|
|
10
20
|
//# sourceMappingURL=as-non-empty-string.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"as-non-empty-string.js","sourceRoot":"","sources":["../../src/common/as-non-empty-string.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"as-non-empty-string.js","sourceRoot":"","sources":["../../src/common/as-non-empty-string.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAClD,CAAC"}
|
|
@@ -2,11 +2,16 @@ import { asNonEmptyString } from './as-non-empty-string.js';
|
|
|
2
2
|
describe('asNonEmptyString', () => {
|
|
3
3
|
it('returns the value for a non-empty string', () => {
|
|
4
4
|
expect(asNonEmptyString('foo')).toBe('foo');
|
|
5
|
-
expect(asNonEmptyString(' ')).toBe(' ');
|
|
6
5
|
expect(asNonEmptyString('0')).toBe('0');
|
|
7
6
|
});
|
|
8
|
-
it('
|
|
7
|
+
it('trims surrounding whitespace from the value it returns', () => {
|
|
8
|
+
expect(asNonEmptyString(' foo ')).toBe('foo');
|
|
9
|
+
expect(asNonEmptyString('\tfoo\n')).toBe('foo');
|
|
10
|
+
});
|
|
11
|
+
it('returns undefined for the empty string and for whitespace only', () => {
|
|
9
12
|
expect(asNonEmptyString('')).toBeUndefined();
|
|
13
|
+
expect(asNonEmptyString(' ')).toBeUndefined();
|
|
14
|
+
expect(asNonEmptyString('\n\t')).toBeUndefined();
|
|
10
15
|
});
|
|
11
16
|
it.each([
|
|
12
17
|
['number', 0],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"as-non-empty-string.test.js","sourceRoot":"","sources":["../../src/common/as-non-empty-string.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"as-non-empty-string.test.js","sourceRoot":"","sources":["../../src/common/as-non-empty-string.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAC7C,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAChD,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC;QACN,CAAC,QAAQ,EAAE,CAAC,CAAC;QACb,CAAC,mBAAmB,EAAE,EAAE,CAAC;QACzB,CAAC,MAAM,EAAE,IAAI,CAAC;QACd,CAAC,WAAW,EAAE,SAAS,CAAC;QACxB,CAAC,cAAc,EAAE,IAAI,CAAC;QACtB,CAAC,eAAe,EAAE,KAAK,CAAC;QACxB,CAAC,QAAQ,EAAE,EAAE,CAAC;QACd,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC,KAAK,EAAE,GAAG,CAAC;KACb,CAAC,CAAC,0BAA0B,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QAC/C,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The outcome of judging a credential's validity window at a point in time.
|
|
3
|
+
*
|
|
4
|
+
* - `pass`: the credential is within its window now. A credential with
|
|
5
|
+
* neither bound is "valid indefinitely" (VCDM 2.0, Validity Period) and
|
|
6
|
+
* passes.
|
|
7
|
+
* - `fail`: the credential is outside its window (`expired`,
|
|
8
|
+
* `not_yet_valid`), or a bound is present but is malformed or in a form
|
|
9
|
+
* this evaluator does not support (`unreadable_bound`). A present bound is
|
|
10
|
+
* never dropped: a credential whose bound cannot be read is not the same
|
|
11
|
+
* as a credential without one.
|
|
12
|
+
*/
|
|
13
|
+
export type ValidityWindowOutcome = {
|
|
14
|
+
result: 'pass';
|
|
15
|
+
} | {
|
|
16
|
+
result: 'fail';
|
|
17
|
+
reason: 'expired' | 'not_yet_valid' | 'unreadable_bound';
|
|
18
|
+
message: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Judges the VCDM 2.0 `validFrom` and `validUntil` claims against `now`,
|
|
22
|
+
* comparing them as points on a timeline as the data model requires.
|
|
23
|
+
*
|
|
24
|
+
* Supported form: an XML Schema `dateTimeStamp` written as
|
|
25
|
+
* `YYYY-MM-DDTHH:MM:SS[.fraction]` followed by `Z` or an offset within
|
|
26
|
+
* `-14:00`..`+14:00`, with uppercase `T` and `Z`, naming a calendar day that
|
|
27
|
+
* exists. XML Schema's `24:00:00` end-of-day form, a lowercase separator, an
|
|
28
|
+
* offset outside that range, an impossible day and any non-string value are
|
|
29
|
+
* unreadable and fail as `unreadable_bound`, naming the bound, rather than
|
|
30
|
+
* being normalised to a date the credential never asserted.
|
|
31
|
+
*
|
|
32
|
+
* Comparison is at millisecond precision: a fraction finer than that in a
|
|
33
|
+
* bound is truncated when the bound is parsed, so a bound within the same
|
|
34
|
+
* millisecond as `now` is treated as reached. `now` must be a valid Date;
|
|
35
|
+
* an invalid one is a caller error and throws rather than judging anything.
|
|
36
|
+
*
|
|
37
|
+
* Verification providers may not enforce these claims for every securing
|
|
38
|
+
* mechanism (the pinned VCKit reads only the JOSE `exp` and `nbf` claims for
|
|
39
|
+
* a `vc+jwt` envelope), so every verification path applies this judgement
|
|
40
|
+
* to the credential's own claims.
|
|
41
|
+
*/
|
|
42
|
+
export declare function evaluateValidityWindow(claims: {
|
|
43
|
+
validFrom?: unknown;
|
|
44
|
+
validUntil?: unknown;
|
|
45
|
+
}, now?: Date): ValidityWindowOutcome;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { asDateTime } from './as-date-time.js';
|
|
2
|
+
/**
|
|
3
|
+
* Judges the VCDM 2.0 `validFrom` and `validUntil` claims against `now`,
|
|
4
|
+
* comparing them as points on a timeline as the data model requires.
|
|
5
|
+
*
|
|
6
|
+
* Supported form: an XML Schema `dateTimeStamp` written as
|
|
7
|
+
* `YYYY-MM-DDTHH:MM:SS[.fraction]` followed by `Z` or an offset within
|
|
8
|
+
* `-14:00`..`+14:00`, with uppercase `T` and `Z`, naming a calendar day that
|
|
9
|
+
* exists. XML Schema's `24:00:00` end-of-day form, a lowercase separator, an
|
|
10
|
+
* offset outside that range, an impossible day and any non-string value are
|
|
11
|
+
* unreadable and fail as `unreadable_bound`, naming the bound, rather than
|
|
12
|
+
* being normalised to a date the credential never asserted.
|
|
13
|
+
*
|
|
14
|
+
* Comparison is at millisecond precision: a fraction finer than that in a
|
|
15
|
+
* bound is truncated when the bound is parsed, so a bound within the same
|
|
16
|
+
* millisecond as `now` is treated as reached. `now` must be a valid Date;
|
|
17
|
+
* an invalid one is a caller error and throws rather than judging anything.
|
|
18
|
+
*
|
|
19
|
+
* Verification providers may not enforce these claims for every securing
|
|
20
|
+
* mechanism (the pinned VCKit reads only the JOSE `exp` and `nbf` claims for
|
|
21
|
+
* a `vc+jwt` envelope), so every verification path applies this judgement
|
|
22
|
+
* to the credential's own claims.
|
|
23
|
+
*/
|
|
24
|
+
export function evaluateValidityWindow(claims, now = new Date()) {
|
|
25
|
+
if (!(now instanceof Date) || Number.isNaN(now.getTime())) {
|
|
26
|
+
throw new TypeError('evaluateValidityWindow requires a valid Date for now');
|
|
27
|
+
}
|
|
28
|
+
const from = readBound(claims.validFrom);
|
|
29
|
+
const until = readBound(claims.validUntil);
|
|
30
|
+
if (from.state === 'readable' && now < from.date) {
|
|
31
|
+
return {
|
|
32
|
+
result: 'fail',
|
|
33
|
+
reason: 'not_yet_valid',
|
|
34
|
+
message: `Credential is not yet valid: validFrom ${from.date.toISOString()} is after ${now.toISOString()}`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (until.state === 'readable' && now > until.date) {
|
|
38
|
+
return {
|
|
39
|
+
result: 'fail',
|
|
40
|
+
reason: 'expired',
|
|
41
|
+
message: `Credential has expired: validUntil ${until.date.toISOString()} is before ${now.toISOString()}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const unreadable = [
|
|
45
|
+
from.state === 'unreadable' ? 'validFrom' : null,
|
|
46
|
+
until.state === 'unreadable' ? 'validUntil' : null,
|
|
47
|
+
].filter((name) => name !== null);
|
|
48
|
+
if (unreadable.length > 0) {
|
|
49
|
+
return {
|
|
50
|
+
result: 'fail',
|
|
51
|
+
reason: 'unreadable_bound',
|
|
52
|
+
message: `${unreadable.join(' and ')} could not be read as a date-time`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return { result: 'pass' };
|
|
56
|
+
}
|
|
57
|
+
const DATE_TIME_STAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-](?:0\d|1[0-3]):[0-5]\d|[+-]14:00)$/;
|
|
58
|
+
function readBound(value) {
|
|
59
|
+
// Only an omitted property is an absent bound; an explicit null is a
|
|
60
|
+
// present value the data model does not allow.
|
|
61
|
+
if (value === undefined)
|
|
62
|
+
return { state: 'absent' };
|
|
63
|
+
if (typeof value !== 'string' || !DATE_TIME_STAMP.test(value))
|
|
64
|
+
return { state: 'unreadable' };
|
|
65
|
+
const date = asDateTime(value);
|
|
66
|
+
return date === undefined ? { state: 'unreadable' } : { state: 'readable', date };
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=validity-window.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validity-window.js","sourceRoot":"","sources":["../../src/common/validity-window.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAkB/C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAqD,EACrD,MAAY,IAAI,IAAI,EAAE;IAEtB,IAAI,CAAC,CAAC,GAAG,YAAY,IAAI,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,SAAS,CAAC,sDAAsD,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACjD,OAAO;YACL,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,eAAe;YACvB,OAAO,EAAE,0CAA0C,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,GAAG,CAAC,WAAW,EAAE,EAAE;SAC3G,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,KAAK,UAAU,IAAI,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QACnD,OAAO;YACL,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,sCAAsC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,GAAG,CAAC,WAAW,EAAE,EAAE;SACzG,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,GAAG;QACjB,IAAI,CAAC,KAAK,KAAK,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;QAChD,KAAK,CAAC,KAAK,KAAK,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI;KACnD,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAClD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,kBAAkB;YAC1B,OAAO,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,mCAAmC;SACxE,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAID,MAAM,eAAe,GAAG,2FAA2F,CAAC;AAEpH,SAAS,SAAS,CAAC,KAAc;IAC/B,qEAAqE;IACrE,+CAA+C;IAC/C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IAC9F,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AACpF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { evaluateValidityWindow } from './validity-window.js';
|
|
2
|
+
const NOW = new Date('2026-09-11T00:00:00.000Z');
|
|
3
|
+
describe('evaluateValidityWindow', () => {
|
|
4
|
+
it('fails an expired credential, naming the bound', () => {
|
|
5
|
+
expect(evaluateValidityWindow({ validFrom: '2020-01-01T00:00:00Z', validUntil: '2021-01-01T00:00:00Z' }, NOW)).toEqual({
|
|
6
|
+
result: 'fail',
|
|
7
|
+
reason: 'expired',
|
|
8
|
+
message: expect.stringContaining('validUntil 2021-01-01T00:00:00.000Z'),
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
it('fails a credential that is not yet valid', () => {
|
|
12
|
+
expect(evaluateValidityWindow({ validFrom: '2035-01-01T00:00:00Z' }, NOW)).toMatchObject({
|
|
13
|
+
result: 'fail',
|
|
14
|
+
reason: 'not_yet_valid',
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
it('passes inside the window, exactly on a bound, and with no bound at all', () => {
|
|
18
|
+
expect(evaluateValidityWindow({ validFrom: '2026-01-01T00:00:00Z', validUntil: '2027-01-01T00:00:00Z' }, NOW)).toEqual({ result: 'pass' });
|
|
19
|
+
expect(evaluateValidityWindow({ validFrom: NOW.toISOString() }, NOW)).toEqual({ result: 'pass' });
|
|
20
|
+
expect(evaluateValidityWindow({ validUntil: NOW.toISOString() }, NOW)).toEqual({ result: 'pass' });
|
|
21
|
+
// VCDM 2.0: with neither bound the credential is valid indefinitely.
|
|
22
|
+
expect(evaluateValidityWindow({}, NOW)).toEqual({ result: 'pass' });
|
|
23
|
+
expect(evaluateValidityWindow({ validFrom: undefined, validUntil: undefined }, NOW)).toEqual({ result: 'pass' });
|
|
24
|
+
expect(evaluateValidityWindow({ validUntil: '2035-01-01T00:00:00+14:00' }, NOW)).toEqual({ result: 'pass' });
|
|
25
|
+
expect(evaluateValidityWindow({ validUntil: '2035-01-01T00:00:00.250-13:59' }, NOW)).toEqual({ result: 'pass' });
|
|
26
|
+
});
|
|
27
|
+
it('fails a bound that is present but not a date-time, and never normalises an impossible date', () => {
|
|
28
|
+
// A violated readable bound wins over an unreadable one; otherwise an
|
|
29
|
+
// unreadable bound fails the credential rather than being dropped.
|
|
30
|
+
expect(evaluateValidityWindow({ validFrom: 'yesterday', validUntil: '2021-01-01T00:00:00Z' }, NOW)).toMatchObject({
|
|
31
|
+
result: 'fail',
|
|
32
|
+
reason: 'expired',
|
|
33
|
+
});
|
|
34
|
+
expect(evaluateValidityWindow({ validFrom: '2020-01-01T00:00:00Z', validUntil: 'never' }, NOW)).toEqual({
|
|
35
|
+
result: 'fail',
|
|
36
|
+
reason: 'unreadable_bound',
|
|
37
|
+
message: 'validUntil could not be read as a date-time',
|
|
38
|
+
});
|
|
39
|
+
expect(evaluateValidityWindow({ validFrom: 12345, validUntil: {} }, NOW)).toMatchObject({
|
|
40
|
+
message: 'validFrom and validUntil could not be read as a date-time',
|
|
41
|
+
});
|
|
42
|
+
// 30 February is not a date; it must not become 2 March and then expire.
|
|
43
|
+
expect(evaluateValidityWindow({ validUntil: '2021-02-30T00:00:00Z' }, NOW)).toMatchObject({
|
|
44
|
+
reason: 'unreadable_bound',
|
|
45
|
+
});
|
|
46
|
+
// XML Schema's end-of-day form is outside the supported profile.
|
|
47
|
+
expect(evaluateValidityWindow({ validUntil: '2030-12-31T24:00:00Z' }, NOW)).toMatchObject({
|
|
48
|
+
reason: 'unreadable_bound',
|
|
49
|
+
});
|
|
50
|
+
// An explicit null is a present value the data model does not allow.
|
|
51
|
+
expect(evaluateValidityWindow({ validUntil: null }, NOW)).toMatchObject({ reason: 'unreadable_bound' });
|
|
52
|
+
// Only the dateTimeStamp form: uppercase separators and offsets within 14 hours.
|
|
53
|
+
expect(evaluateValidityWindow({ validUntil: '2035-01-01t00:00:00z' }, NOW)).toMatchObject({
|
|
54
|
+
reason: 'unreadable_bound',
|
|
55
|
+
});
|
|
56
|
+
expect(evaluateValidityWindow({ validUntil: '2035-01-01T00:00:00+23:59' }, NOW)).toMatchObject({
|
|
57
|
+
reason: 'unreadable_bound',
|
|
58
|
+
});
|
|
59
|
+
expect(evaluateValidityWindow({ validUntil: '2035-01-01T00:00:00+14:01' }, NOW)).toMatchObject({
|
|
60
|
+
reason: 'unreadable_bound',
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
it('refuses an invalid clock rather than judging against it', () => {
|
|
64
|
+
expect(() => evaluateValidityWindow({ validUntil: '2021-01-01T00:00:00Z' }, new Date('not a date'))).toThrow(TypeError);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=validity-window.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validity-window.test.js","sourceRoot":"","sources":["../../src/common/validity-window.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC;AAEjD,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CACJ,sBAAsB,CAAC,EAAE,SAAS,EAAE,sBAAsB,EAAE,UAAU,EAAE,sBAAsB,EAAE,EAAE,GAAG,CAAC,CACvG,CAAC,OAAO,CAAC;YACR,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,MAAM,CAAC,gBAAgB,CAAC,qCAAqC,CAAC;SACxE,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,sBAAsB,CAAC,EAAE,SAAS,EAAE,sBAAsB,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC;YACvF,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,eAAe;SACxB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,CACJ,sBAAsB,CAAC,EAAE,SAAS,EAAE,sBAAsB,EAAE,UAAU,EAAE,sBAAsB,EAAE,EAAE,GAAG,CAAC,CACvG,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9B,MAAM,CAAC,sBAAsB,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAClG,MAAM,CAAC,sBAAsB,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,WAAW,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACnG,qEAAqE;QACrE,MAAM,CAAC,sBAAsB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACpE,MAAM,CAAC,sBAAsB,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACjH,MAAM,CAAC,sBAAsB,CAAC,EAAE,UAAU,EAAE,2BAA2B,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7G,MAAM,CAAC,sBAAsB,CAAC,EAAE,UAAU,EAAE,+BAA+B,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACnH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4FAA4F,EAAE,GAAG,EAAE;QACpG,sEAAsE;QACtE,mEAAmE;QACnE,MAAM,CAAC,sBAAsB,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,sBAAsB,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC;YAChH,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,SAAS;SAClB,CAAC,CAAC;QACH,MAAM,CAAC,sBAAsB,CAAC,EAAE,SAAS,EAAE,sBAAsB,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;YACtG,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,kBAAkB;YAC1B,OAAO,EAAE,6CAA6C;SACvD,CAAC,CAAC;QACH,MAAM,CAAC,sBAAsB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC;YACtF,OAAO,EAAE,2DAA2D;SACrE,CAAC,CAAC;QACH,yEAAyE;QACzE,MAAM,CAAC,sBAAsB,CAAC,EAAE,UAAU,EAAE,sBAAsB,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC;YACxF,MAAM,EAAE,kBAAkB;SAC3B,CAAC,CAAC;QACH,iEAAiE;QACjE,MAAM,CAAC,sBAAsB,CAAC,EAAE,UAAU,EAAE,sBAAsB,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC;YACxF,MAAM,EAAE,kBAAkB;SAC3B,CAAC,CAAC;QACH,qEAAqE;QACrE,MAAM,CAAC,sBAAsB,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC;QACxG,iFAAiF;QACjF,MAAM,CAAC,sBAAsB,CAAC,EAAE,UAAU,EAAE,sBAAsB,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC;YACxF,MAAM,EAAE,kBAAkB;SAC3B,CAAC,CAAC;QACH,MAAM,CAAC,sBAAsB,CAAC,EAAE,UAAU,EAAE,2BAA2B,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC;YAC7F,MAAM,EAAE,kBAAkB;SAC3B,CAAC,CAAC;QACH,MAAM,CAAC,sBAAsB,CAAC,EAAE,UAAU,EAAE,2BAA2B,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC;YAC7F,MAAM,EAAE,kBAAkB;SAC3B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,CAAC,GAAG,EAAE,CAAC,sBAAsB,CAAC,EAAE,UAAU,EAAE,sBAAsB,EAAE,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAC1G,SAAS,CACV,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/build/loaders/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { SchemaLoaderError, SchemaLoaderNetworkError, SchemaLoaderHttpError, SchemaLoaderInvalidJsonError, } from './errors.js';
|
|
2
2
|
export { createSchemaLoader, type SchemaLoader } from './schema-loader.js';
|
|
3
3
|
export { createJsonLdDocumentLoader, type JsonLdDocumentLoaderOptions, type LoadedRemoteDocument, } from './jsonld-document-loader.js';
|
|
4
|
+
export { isHostDeliveryFailure } from '../bundle/fallback.js';
|
|
5
|
+
export type { BundledFallbackEvent, BundledFallbackOptions } from '../bundle/fallback.js';
|
package/build/loaders/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { SchemaLoaderError, SchemaLoaderNetworkError, SchemaLoaderHttpError, SchemaLoaderInvalidJsonError, } from './errors.js';
|
|
2
2
|
export { createSchemaLoader } from './schema-loader.js';
|
|
3
3
|
export { createJsonLdDocumentLoader, } from './jsonld-document-loader.js';
|
|
4
|
+
export { isHostDeliveryFailure } from '../bundle/fallback.js';
|
|
4
5
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/loaders/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAqB,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EACL,0BAA0B,GAG3B,MAAM,6BAA6B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/loaders/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAqB,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EACL,0BAA0B,GAG3B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ResolveDocumentOptions } from '../resolvers/index.js';
|
|
2
|
+
import type { BundledFallbackOptions } from '../bundle/fallback.js';
|
|
2
3
|
import type { TtlCache } from '../cache/ttl-cache.js';
|
|
3
4
|
/**
|
|
4
5
|
* The subset of jsonld's `RemoteDocument` shape this loader returns. Carries
|
|
@@ -13,17 +14,18 @@ export interface LoadedRemoteDocument {
|
|
|
13
14
|
document: unknown;
|
|
14
15
|
}
|
|
15
16
|
/** Options for {@link createJsonLdDocumentLoader}. */
|
|
16
|
-
export interface JsonLdDocumentLoaderOptions extends ResolveDocumentOptions {
|
|
17
|
+
export interface JsonLdDocumentLoaderOptions extends ResolveDocumentOptions, BundledFallbackOptions {
|
|
17
18
|
/** `Accept` header for `@context` fetches. Defaults to JSON-LD then JSON. */
|
|
18
19
|
accept?: string;
|
|
19
20
|
/**
|
|
20
21
|
* Optional cache, keyed by URL, for resolved `@context` documents. When
|
|
21
22
|
* supplied, a document fetched once is reused for the cache's TTL rather
|
|
22
|
-
* than re-fetched on every expansion. Only successful
|
|
23
|
+
* than re-fetched on every expansion. Only successful loads are cached
|
|
23
24
|
* (the {@link TtlCache} contract does not cache rejected fetches), so a
|
|
24
|
-
* URL the guard rejects is re-checked every time
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* URL the guard rejects is re-checked every time; a bundled copy served
|
|
26
|
+
* after a host failure counts as a successful load and is cached like a
|
|
27
|
+
* fetched one. A successfully loaded document is cached even if
|
|
28
|
+
* downstream JSON-LD processing later rejects it.
|
|
27
29
|
*/
|
|
28
30
|
cache?: TtlCache<LoadedRemoteDocument>;
|
|
29
31
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withBundledFallback } from '../bundle/fallback.js';
|
|
1
2
|
/** Default `Accept` for `@context` documents: JSON-LD, falling back to JSON. */
|
|
2
3
|
const CONTEXT_ACCEPT_HEADER = 'application/ld+json, application/json;q=0.9';
|
|
3
4
|
/**
|
|
@@ -17,8 +18,8 @@ const CONTEXT_ACCEPT_HEADER = 'application/ld+json, application/json;q=0.9';
|
|
|
17
18
|
* @see https://github.com/uncefact/tests-untp/issues/707
|
|
18
19
|
*/
|
|
19
20
|
export function createJsonLdDocumentLoader(options) {
|
|
20
|
-
const { cache, accept, ...resolverOptions } = options ?? {};
|
|
21
|
-
const
|
|
21
|
+
const { cache, accept, bundledFallback, onBundledFallback, ...resolverOptions } = options ?? {};
|
|
22
|
+
const fetchContext = async (url) => {
|
|
22
23
|
// Lazy import: the resolver stack pulls in undici, which jsdom test
|
|
23
24
|
// environments cannot evaluate, so it loads at fetch time to keep this
|
|
24
25
|
// module importable there.
|
|
@@ -29,6 +30,14 @@ export function createJsonLdDocumentLoader(options) {
|
|
|
29
30
|
});
|
|
30
31
|
return { documentUrl: finalUrl, document: json };
|
|
31
32
|
};
|
|
33
|
+
// A bundled context is served under the URL that was requested, since no
|
|
34
|
+
// redirect was followed to reach it.
|
|
35
|
+
const load = async (url) => {
|
|
36
|
+
const fetched = await withBundledFallback(url, { bundledFallback, onBundledFallback }, () => fetchContext(url));
|
|
37
|
+
return 'documentUrl' in fetched && 'document' in fetched
|
|
38
|
+
? fetched
|
|
39
|
+
: { documentUrl: url, document: fetched };
|
|
40
|
+
};
|
|
32
41
|
return cache ? (url) => cache.get(url, () => load(url)) : load;
|
|
33
42
|
}
|
|
34
43
|
//# sourceMappingURL=jsonld-document-loader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonld-document-loader.js","sourceRoot":"","sources":["../../src/loaders/jsonld-document-loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"jsonld-document-loader.js","sourceRoot":"","sources":["../../src/loaders/jsonld-document-loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAiC5D,gFAAgF;AAChF,MAAM,qBAAqB,GAAG,6CAA6C,CAAC;AAE5E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAAqC;IAErC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,iBAAiB,EAAE,GAAG,eAAe,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;IAEhG,MAAM,YAAY,GAAG,KAAK,EAAE,GAAW,EAAiC,EAAE;QACxE,oEAAoE;QACpE,uEAAuE;QACvE,2BAA2B;QAC3B,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC;QACtE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,mBAAmB,CAAC,GAAG,EAAE;YACxD,GAAG,eAAe;YAClB,MAAM,EAAE,MAAM,IAAI,qBAAqB;SACxC,CAAC,CAAC;QACH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACnD,CAAC,CAAC;IAEF,yEAAyE;IACzE,qCAAqC;IACrC,MAAM,IAAI,GAAG,KAAK,EAAE,GAAW,EAAiC,EAAE;QAChE,MAAM,OAAO,GAAG,MAAM,mBAAmB,CACvC,GAAG,EACH,EAAE,eAAe,EAAE,iBAAiB,EAAE,EACtC,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CACxB,CAAC;QACF,OAAO,aAAa,IAAI,OAAO,IAAI,UAAU,IAAI,OAAO;YACtD,CAAC,CAAE,OAAgC;YACnC,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAC9C,CAAC,CAAC;IAEF,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjE,CAAC"}
|