Skip to content

Commit

Permalink
Jsr.Fetch.Pkg.info ← specific version
Browse files Browse the repository at this point in the history
  • Loading branch information
philcockfield committed Nov 16, 2024
1 parent 5203d79 commit 0074172
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 41 deletions.
88 changes: 61 additions & 27 deletions code/sys.driver/driver-vitepress/src/m.Jsr/-.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Semver } from '@sys/std/semver';
import { describe, expect, it, rx, slug } from '../-test.ts';
import { describe, expect, it, rx, slug, Testing } from '../-test.ts';
import { Fetch, Jsr } from './mod.ts';

describe('Jsr', () => {
Expand All @@ -10,29 +10,32 @@ describe('Jsr', () => {
describe('Jsr.Fetch.Pkg', () => {
describe('Pkg.versions( name )', () => {
it('200 - list', async () => {
const name = '@sys/std';
const res = await Fetch.Pkg.versions(name);
await Testing.retry(3, async () => {
const name = '@sys/std';
const res = await Fetch.Pkg.versions(name);
expect(res.url).to.eql(`https://jsr.io/${name}/meta.json`);
expect(res.status).to.eql(200);
expect(res.error).to.eql(undefined);

expect(res.url).to.eql(`https://jsr.io/${name}/meta.json`);
expect(res.status).to.eql(200);
expect(res.error).to.eql(undefined);

expect(res.data?.scope).to.eql('sys');
expect(res.data?.name).to.eql('std');
expect(res.data?.scope).to.eql('sys');
expect(res.data?.name).to.eql('std');

const versions = Object.keys(res.data?.versions ?? []);
expect(versions).to.include('0.0.1');
expect(versions).to.include('0.0.42');
expect(Semver.Is.valid(res.data?.latest)).to.eql(true);
const versions = Object.keys(res.data?.versions ?? []);
expect(versions).to.include('0.0.1');
expect(versions).to.include('0.0.42');
expect(Semver.Is.valid(res.data?.latest)).to.eql(true);
});
});

it('404: module does not exist', async () => {
const name = `@foo/${slug()}-${slug()}`;
const res = await Fetch.Pkg.versions(name);
expect(res.status).to.eql(404);
expect(res.data).to.eql(undefined);
expect(res.error?.name).to.eql('HttpError');
expect(res.error?.message).to.include('404 Not Found');
await Testing.retry(3, async () => {
const name = `@foo/${slug()}-${slug()}`;
const res = await Fetch.Pkg.versions(name);
expect(res.status).to.eql(404);
expect(res.data).to.eql(undefined);
expect(res.error?.name).to.eql('HttpError');
expect(res.error?.message).to.include('404 Not Found');
});
});
});

Expand All @@ -47,7 +50,45 @@ describe('Jsr', () => {
*/

describe('Pkg.info( name, version )', () => {
it.skip('200 - success', async () => {});
it('200 - success', async () => {
await Testing.retry(3, async () => {
const name = '@sys/std';
const version = '0.0.42';
const res = await Fetch.Pkg.info(name, version);
expect(res.status).to.eql(200);
expect(res.error).to.eql(undefined);

expect(res.data?.scope).to.eql('@sys');
expect(res.data?.name).to.eql('std');
expect(res.data?.version).to.eql(version);

const manifest = res.data?.manifest ?? {};
const paths = Object.keys(manifest);

const assertExists = (path: string) => expect(paths.includes(path)).to.eql(true);
assertExists('/README.md');
assertExists('/src/mod.ts');
assertExists('/src/pkg.ts');
assertExists('/src/m.Immutable/t.ts');

const mod = manifest['/src/mod.ts'];
expect(mod.checksum.startsWith('sha256-')).to.eql(true);
expect(typeof mod.size === 'number').to.eql(true);
});
});

it('latest: version ommited', async () => {
const name = '@sys/std';
const latest = (await Fetch.Pkg.versions(name)).data?.latest;

const res = await Fetch.Pkg.info(name);
expect(res.status).to.eql(200);
expect(res.error).to.eql(undefined);

expect(res.data?.scope).to.eql('@sys');
expect(res.data?.name).to.eql('std');
expect(res.data?.version).to.eql(latest);
});
});

it('dispose ← (cancel fetch operation)', async () => {
Expand All @@ -60,12 +101,5 @@ describe('Jsr', () => {
expect(res.data).to.eql(undefined);
expect(res.error?.message).to.include('Fetch operation disposed of before completing (499)');
});

it.skip('', async () => {});
it.skip('', async () => {});
it.skip('', async () => {});
it.skip('', async () => {});
it.skip('', async () => {});
it.skip('', async () => {});
});
});
26 changes: 24 additions & 2 deletions code/sys.driver/driver-vitepress/src/m.Jsr/m.Fetch.Pkg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,39 @@ export const Pkg: t.JsrFetchPkgLib = {
versions(name, options = {}) {
const url = wrangle.url(name);
const fetch = Fetch.disposable(options.dispose$);
return fetch.json<t.JsrPackageMeta>(url);
return fetch.json<t.JsrPkgMetaVersions>(url);
},

/**
* https://jsr.io/docs/api#package-version-metadata
*/
async info(name, version, options = {}) {
if (!version) version = (await Pkg.versions(name)).data?.latest;
const url = wrangle.url(name, version);
const fetch = Fetch.disposable(options.dispose$);
let res = await fetch.json<t.JsrPkgVersionInfo>(url);
if (res.data) {
const data = {
...res.data,
scope: wrangle.scope(name),
name: wrangle.name(name),
version: version ?? '',
};
res = { ...res, data };
}
return res;
},
};

/**
* Helpers
*/
const wrangle = {
url(name: string, version?: string) {
url(name: string, version?: t.StringSemVer) {
const base = `https://jsr.io/${name}`;
const path = version ? `${version}_meta.json` : 'meta.json';
return `${base}/${path}`;
},
scope: (input: string) => input.split('/')[0],
name: (input: string) => input.split('/')[1],
} as const;
47 changes: 35 additions & 12 deletions code/sys.driver/driver-vitepress/src/m.Jsr/t.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,53 @@ export type JsrFetchLib = {
* Network fetching helpers against a specific package.
*/
export type JsrFetchPkgLib = {
/** Retrieve the package's latest version and version history. */
versions(name: string, options?: t.JsrFetchVersionsOptions): Promise<t.JsrFetchVersionsResponse>;
/**
* Retrieve the package's latest version and version history.
*/
versions(name: string, options?: t.JsrFetchPkgOptions): Promise<t.JsrFetchPkgVersionsResponse>;

/**
* Retrieve meta-data about a specific package version.
*/
info(
name: string,
version?: t.StringSemVer,
options?: t.JsrFetchPkgOptions,
): Promise<t.JsrFetchPkgInfoResponse>;
};

/** Options for the `Jsr.Fetch.versions` method */
export type JsrFetchVersionsOptions = { dispose$?: t.UntilObservable };
export type JsrFetchPkgOptions = { dispose$?: t.UntilObservable };
/** Resposne to a `Jsr.Fetch.Pkg.versions` request. */
export type JsrFetchPkgVersionsResponse = t.FetchResponse<t.JsrPkgMetaVersions>;

/** Resposne to a `Jsr.Fetch.versions` request. */
export type JsrFetchVersionsResponse = t.FetchResponse<t.JsrPackageMeta>;
/** Response to a `Jsr.Fetch.Pkg.info` request. */
export type JsrFetchPkgInfoResponse = t.FetchResponse<t.JsrPkgVersionInfo>;

/**
* The meta-data about a module.
* Top level meta-data about a published package including it's version history.
* https://jsr.io/docs/api#package-metadata
*
* ```
* GET: https://jsr.io/@<scope>/<package-name>/meta.json
* ```
*/
export type JsrPackageMeta = {
export type JsrPkgMetaVersions = {
scope: string;
name: string;
latest: t.SemVer;
latest: t.StringSemVer;
versions: { [version: string]: JsrPackageMetaVersion };
};
/** Version details about a specific package version. */
export type JsrPackageMetaVersion = { yanked?: boolean };

/**
* Meta-data about a specific published package version.
* https://jsr.io/docs/api#package-version-metadata
*/
export type JsrPkgVersionInfo = {
scope: string;
name: string;
version: t.StringSemVer;
manifest: JsrPkgManifest;
exports: { [key: string]: string };
};

export type JsrPkgManifest = { [path: string]: JsrPkgManifestFile };
export type JsrPkgManifestFile = { size: number; checksum: string };

0 comments on commit 0074172

Please sign in to comment.