@webboxes/plugin-hub 0.0.0-stage → 0.1.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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +8 -2
  3. package/dist/contract.d.ts +63 -0
  4. package/dist/contract.d.ts.map +1 -0
  5. package/dist/contract.js +310 -0
  6. package/dist/contract.js.map +1 -0
  7. package/dist/folders.d.ts +28 -0
  8. package/dist/folders.d.ts.map +1 -0
  9. package/dist/folders.js +88 -0
  10. package/dist/folders.js.map +1 -0
  11. package/dist/github-cli.d.ts +18 -0
  12. package/dist/github-cli.d.ts.map +1 -0
  13. package/dist/github-cli.js +140 -0
  14. package/dist/github-cli.js.map +1 -0
  15. package/dist/http.d.ts +17 -0
  16. package/dist/http.d.ts.map +1 -0
  17. package/dist/http.js +57 -0
  18. package/dist/http.js.map +1 -0
  19. package/dist/hub.d.ts +129 -0
  20. package/dist/hub.d.ts.map +1 -0
  21. package/dist/hub.js +148 -0
  22. package/dist/hub.js.map +1 -0
  23. package/dist/index.d.ts +17 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +20 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/name.d.ts +31 -0
  28. package/dist/name.d.ts.map +1 -0
  29. package/dist/name.js +57 -0
  30. package/dist/name.js.map +1 -0
  31. package/dist/npm-cli.d.ts +11 -0
  32. package/dist/npm-cli.d.ts.map +1 -0
  33. package/dist/npm-cli.js +102 -0
  34. package/dist/npm-cli.js.map +1 -0
  35. package/dist/npm-publish.d.ts +36 -0
  36. package/dist/npm-publish.d.ts.map +1 -0
  37. package/dist/npm-publish.js +63 -0
  38. package/dist/npm-publish.js.map +1 -0
  39. package/dist/pack.d.ts +32 -0
  40. package/dist/pack.d.ts.map +1 -0
  41. package/dist/pack.js +137 -0
  42. package/dist/pack.js.map +1 -0
  43. package/dist/pull.d.ts +53 -0
  44. package/dist/pull.d.ts.map +1 -0
  45. package/dist/pull.js +121 -0
  46. package/dist/pull.js.map +1 -0
  47. package/dist/save.d.ts +10 -0
  48. package/dist/save.d.ts.map +1 -0
  49. package/dist/save.js +15 -0
  50. package/dist/save.js.map +1 -0
  51. package/dist/share.d.ts +65 -0
  52. package/dist/share.d.ts.map +1 -0
  53. package/dist/share.js +133 -0
  54. package/dist/share.js.map +1 -0
  55. package/dist/source.d.ts +38 -0
  56. package/dist/source.d.ts.map +1 -0
  57. package/dist/source.js +122 -0
  58. package/dist/source.js.map +1 -0
  59. package/dist/store.d.ts +43 -0
  60. package/dist/store.d.ts.map +1 -0
  61. package/dist/store.js +112 -0
  62. package/dist/store.js.map +1 -0
  63. package/dist/tarball.d.ts +16 -0
  64. package/dist/tarball.d.ts.map +1 -0
  65. package/dist/tarball.js +138 -0
  66. package/dist/tarball.js.map +1 -0
  67. package/dist/version.d.ts +16 -0
  68. package/dist/version.d.ts.map +1 -0
  69. package/dist/version.js +69 -0
  70. package/dist/version.js.map +1 -0
  71. package/package.json +44 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sIAAsI;IACtI,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,8IAA8I;IAC9I,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAA;IAC/D,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAA;IAC/D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,oCAAoC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAOD,qBAAa,kBAAmB,SAAQ,KAAK;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM;gBAA5C,UAAU,EAAE,MAAM,EAAW,OAAO,EAAE,MAAM;CAIlE;AAYD,qBAAa,WAAW;IACV,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;IAEjC,OAAO,KAAK,SAAS,GAAmD;IAExE,OAAO,CAAC,IAAI;IAOZ,wEAAwE;IACxE,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAKlD;;;OAGG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,CAAC,EAAE,GAAG,OAAa,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;KAAE;IAgB3L,qIAAqI;IACrI,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAa9D,mDAAmD;IACnD,IAAI,IAAI,aAAa,EAAE;CAgBxB"}
package/dist/store.js ADDED
@@ -0,0 +1,112 @@
1
+ // The local plugin store (spec 044 lifecycle.md, "Save" and "Pull"): every version saved or pulled on this machine, as its tarball plus a record,
2
+ // under one folder of the app instance's home. Any app instance on the machine can install from it offline, and a rollback is a pull of an
3
+ // older version from it. A version is kept once: the same name and version with different content is refused, never overwritten.
4
+ //
5
+ // <root>/index.json { formatVersion: 1, plugins: { "<name>": { "<version>": StoreRecord } } }
6
+ // <root>/<@owner>/<name>/<version>.tgz (unscoped: <root>/<name>/<version>.tgz)
7
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
8
+ import { dirname, join } from 'node:path';
9
+ import { parsePluginName, storeDirectoryOf } from './name.js';
10
+ import { integrityOf } from './tarball.js';
11
+ import { compareVersions, newestVersion, parseVersion } from './version.js';
12
+ export class StoreConflictError extends Error {
13
+ pluginName;
14
+ version;
15
+ constructor(pluginName, version) {
16
+ super(`${pluginName}@${version} is already in the store with different content; a version is kept once, so give the change a new version`);
17
+ this.pluginName = pluginName;
18
+ this.version = version;
19
+ this.name = 'StoreConflictError';
20
+ }
21
+ }
22
+ function writeAtomic(path, bytes) {
23
+ mkdirSync(dirname(path), { recursive: true });
24
+ const temporary = `${path}.${process.pid}.${Date.now()}.tmp`;
25
+ writeFileSync(temporary, bytes);
26
+ try {
27
+ renameSync(temporary, path);
28
+ }
29
+ catch (cause) {
30
+ rmSync(temporary, { force: true });
31
+ throw cause;
32
+ }
33
+ }
34
+ export class PluginStore {
35
+ root;
36
+ constructor(root) {
37
+ this.root = root;
38
+ }
39
+ get indexPath() { return join(this.root, 'index.json'); }
40
+ read() {
41
+ if (!existsSync(this.indexPath))
42
+ return { formatVersion: 1, plugins: {} };
43
+ const parsed = JSON.parse(readFileSync(this.indexPath, 'utf8'));
44
+ if (parsed.formatVersion !== 1 || typeof parsed.plugins !== 'object' || parsed.plugins === null)
45
+ throw new Error(`${this.indexPath} is not a plugin store index (formatVersion 1)`);
46
+ return { formatVersion: 1, plugins: parsed.plugins };
47
+ }
48
+ /** Where a version's tarball lives, whether or not it is stored yet. */
49
+ tarballPath(name, version) {
50
+ if (parseVersion(version) === undefined)
51
+ throw new Error(`${JSON.stringify(version)} is not a semantic version`);
52
+ return join(this.root, ...storeDirectoryOf(parsePluginName(name)), `${version}.tgz`);
53
+ }
54
+ /**
55
+ * Keep a tarball. Storing the same bytes again is a no-op (`added: false`); different bytes under a stored version are refused.
56
+ * The tarball is written before the index, so an interrupted put leaves at most an unlisted file, never a listed version without one.
57
+ */
58
+ put(name, version, tarball, record, now = new Date()) {
59
+ const integrity = integrityOf(tarball);
60
+ const index = this.read();
61
+ const existing = index.plugins[name]?.[version];
62
+ const path = this.tarballPath(name, version);
63
+ if (existing !== undefined) {
64
+ if (existing.integrity !== integrity)
65
+ throw new StoreConflictError(name, version);
66
+ if (existsSync(path))
67
+ return { added: false, stored: { name, version, path, ...existing } };
68
+ }
69
+ writeAtomic(path, tarball);
70
+ const full = { ...record, integrity, storedAt: (existing?.storedAt ?? now.toISOString()) };
71
+ const plugins = { ...index.plugins, [name]: { ...index.plugins[name], [version]: full } };
72
+ writeAtomic(this.indexPath, `${JSON.stringify({ formatVersion: 1, plugins }, null, 2)}\n`);
73
+ return { added: true, stored: { name, version, path, ...full } };
74
+ }
75
+ /** One version, or the newest when no version is given. A listed version whose file is gone or changed is reported, not returned. */
76
+ get(name, version) {
77
+ const versions = this.read().plugins[name];
78
+ if (versions === undefined)
79
+ return undefined;
80
+ const chosen = version ?? newestVersion(Object.keys(versions));
81
+ if (chosen === undefined)
82
+ return undefined;
83
+ const record = versions[chosen];
84
+ if (record === undefined)
85
+ return undefined;
86
+ const path = this.tarballPath(name, chosen);
87
+ if (!existsSync(path))
88
+ throw new Error(`the store lists ${name}@${chosen} but its file ${path} is missing`);
89
+ if (integrityOf(readFileSync(path)) !== record.integrity)
90
+ throw new Error(`the stored file of ${name}@${chosen} no longer matches its checksum; pull it again`);
91
+ return { name, version: chosen, path, ...record };
92
+ }
93
+ /** Every stored version, by name, newest first. */
94
+ list() {
95
+ const out = [];
96
+ const index = this.read();
97
+ for (const name of Object.keys(index.plugins).sort()) {
98
+ const versions = index.plugins[name] ?? {};
99
+ const ordered = Object.keys(versions).flatMap(text => {
100
+ const parsed = parseVersion(text);
101
+ return parsed === undefined ? [] : [parsed];
102
+ }).sort((a, b) => compareVersions(b, a));
103
+ for (const version of ordered) {
104
+ const record = versions[version.text];
105
+ if (record !== undefined)
106
+ out.push({ name, version: version.text, path: this.tarballPath(name, version.text), ...record });
107
+ }
108
+ }
109
+ return out;
110
+ }
111
+ }
112
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export interface TarballFile {
2
+ /** Path inside the package, with `/` separators and no `package/` prefix. */
3
+ readonly path: string;
4
+ readonly content: Uint8Array;
5
+ readonly executable?: boolean;
6
+ }
7
+ /** Pack files into a gzipped tarball, `package/` prefixed and sorted by path. */
8
+ export declare function writeTarball(files: readonly TarballFile[]): Buffer;
9
+ /**
10
+ * Read a gzipped tarball's regular files, with the first path level (`package/` in an npm tarball) removed. Directory, link and global entries
11
+ * are skipped; a path that would leave the package (`..`, absolute) is refused, so a crafted tarball cannot point outside it.
12
+ */
13
+ export declare function readTarball(gzipped: Uint8Array, maxBytes?: number): TarballFile[];
14
+ /** The Subresource Integrity form npm and pnpm record: `sha512-<base64>`. */
15
+ export declare function integrityOf(bytes: Uint8Array): string;
16
+ //# sourceMappingURL=tarball.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tarball.d.ts","sourceRoot":"","sources":["../src/tarball.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,WAAW;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAC9B;AAkDD,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAgBlE;AAuBD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,SAAmB,GAAG,WAAW,EAAE,CA0B3F;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAErD"}
@@ -0,0 +1,138 @@
1
+ // The tarball every plugin travels as (spec 044 lifecycle.md, "One unit: the tarball"): a gzipped ustar archive whose files sit under
2
+ // `package/`, the layout `npm pack` writes and pnpm, npm and DSH's installer read. Written here rather than by `npm pack`, because the shipped
3
+ // app has no npm; and written deterministically (sorted entries, fixed times and owners, a fixed gzip header), so the same files always give
4
+ // the same sha512 on every system and a re-pack of unchanged source is recognised as the same version.
5
+ import { createHash } from 'node:crypto';
6
+ import { gunzipSync, gzipSync } from 'node:zlib';
7
+ /** npm's fixed time for every packed file (1985-10-26T08:15:00Z), so packs do not differ by when they were made. */
8
+ const FIXED_MTIME = 499_162_500;
9
+ const BLOCK = 512;
10
+ /** Byte 9 of a gzip header names the operating system; zlib writes a different one per platform, which would change the checksum. */
11
+ const GZIP_OS_BYTE = 9;
12
+ const GZIP_OS_UNIX = 3;
13
+ function writeString(header, offset, length, value) {
14
+ const bytes = Buffer.from(value, 'utf8');
15
+ if (bytes.length > length)
16
+ throw new Error(`tar header field too long: ${value}`);
17
+ header.set(bytes, offset);
18
+ }
19
+ function writeOctal(header, offset, length, value) {
20
+ writeString(header, offset, length, `${value.toString(8).padStart(length - 1, '0')}\0`);
21
+ }
22
+ /** Split a long path into ustar's 155-byte prefix and 100-byte name at a `/`. */
23
+ function splitPath(path) {
24
+ if (Buffer.byteLength(path) <= 100)
25
+ return { prefix: '', name: path };
26
+ for (let index = path.indexOf('/'); index !== -1; index = path.indexOf('/', index + 1)) {
27
+ const prefix = path.slice(0, index);
28
+ const name = path.slice(index + 1);
29
+ if (Buffer.byteLength(prefix) <= 155 && Buffer.byteLength(name) <= 100)
30
+ return { prefix, name };
31
+ }
32
+ throw new Error(`path too long to pack: ${path}`);
33
+ }
34
+ function header(path, size, mode) {
35
+ const block = new Uint8Array(BLOCK);
36
+ const { prefix, name } = splitPath(path);
37
+ writeString(block, 0, 100, name);
38
+ writeOctal(block, 100, 8, mode);
39
+ writeOctal(block, 108, 8, 0);
40
+ writeOctal(block, 116, 8, 0);
41
+ writeOctal(block, 124, 12, size);
42
+ writeOctal(block, 136, 12, FIXED_MTIME);
43
+ block.fill(0x20, 148, 156);
44
+ block[156] = 0x30;
45
+ writeString(block, 257, 6, 'ustar\0');
46
+ writeString(block, 263, 2, '00');
47
+ writeString(block, 345, 155, prefix);
48
+ let sum = 0;
49
+ for (const byte of block)
50
+ sum += byte;
51
+ writeString(block, 148, 8, `${sum.toString(8).padStart(6, '0')}\0 `);
52
+ return block;
53
+ }
54
+ /** Pack files into a gzipped tarball, `package/` prefixed and sorted by path. */
55
+ export function writeTarball(files) {
56
+ const sorted = [...files].sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
57
+ const chunks = [];
58
+ const seen = new Set();
59
+ for (const file of sorted) {
60
+ if (seen.has(file.path))
61
+ throw new Error(`duplicate path in package: ${file.path}`);
62
+ seen.add(file.path);
63
+ chunks.push(header(`package/${file.path}`, file.content.length, file.executable === true ? 0o755 : 0o644));
64
+ chunks.push(file.content);
65
+ const pad = (BLOCK - (file.content.length % BLOCK)) % BLOCK;
66
+ if (pad > 0)
67
+ chunks.push(new Uint8Array(pad));
68
+ }
69
+ chunks.push(new Uint8Array(BLOCK * 2));
70
+ const gzipped = gzipSync(Buffer.concat(chunks), { level: 9 });
71
+ gzipped[GZIP_OS_BYTE] = GZIP_OS_UNIX;
72
+ return gzipped;
73
+ }
74
+ function readString(block, offset, length) {
75
+ const slice = block.subarray(offset, offset + length);
76
+ const end = slice.indexOf(0);
77
+ return Buffer.from(end === -1 ? slice : slice.subarray(0, end)).toString('utf8');
78
+ }
79
+ function readOctal(block, offset, length) {
80
+ const text = readString(block, offset, length).trim();
81
+ return text === '' ? 0 : Number.parseInt(text, 8);
82
+ }
83
+ /** Paths a pax extended header sets (`path=...`), the form `npm pack` uses for long names. */
84
+ function paxPath(content) {
85
+ const text = Buffer.from(content).toString('utf8');
86
+ for (const record of text.split('\n')) {
87
+ const match = /^\d+ path=(.*)$/u.exec(record);
88
+ if (match?.[1] !== undefined)
89
+ return match[1];
90
+ }
91
+ return undefined;
92
+ }
93
+ /**
94
+ * Read a gzipped tarball's regular files, with the first path level (`package/` in an npm tarball) removed. Directory, link and global entries
95
+ * are skipped; a path that would leave the package (`..`, absolute) is refused, so a crafted tarball cannot point outside it.
96
+ */
97
+ export function readTarball(gzipped, maxBytes = 64 * 1024 * 1024) {
98
+ const archive = gunzipSync(gzipped, { maxOutputLength: maxBytes });
99
+ const files = [];
100
+ let offset = 0;
101
+ let nextPath;
102
+ while (offset + BLOCK <= archive.length) {
103
+ const block = archive.subarray(offset, offset + BLOCK);
104
+ if (block.every(byte => byte === 0))
105
+ break;
106
+ const size = readOctal(block, 124, 12);
107
+ const type = String.fromCharCode(block[156] ?? 0);
108
+ const prefix = readString(block, 345, 155);
109
+ const name = readString(block, 0, 100);
110
+ const content = archive.subarray(offset + BLOCK, offset + BLOCK + size);
111
+ offset += BLOCK + Math.ceil(size / BLOCK) * BLOCK;
112
+ if (type === 'x') {
113
+ nextPath = paxPath(content);
114
+ continue;
115
+ }
116
+ if (type === 'L') {
117
+ nextPath = Buffer.from(content).toString('utf8').replace(/\0+$/u, '');
118
+ continue;
119
+ }
120
+ const fullPath = nextPath ?? (prefix === '' ? name : `${prefix}/${name}`);
121
+ nextPath = undefined;
122
+ if (type !== '0' && type !== '\0')
123
+ continue;
124
+ const parts = fullPath.split('/').filter(part => part !== '' && part !== '.');
125
+ if (fullPath.startsWith('/') || parts.includes('..'))
126
+ throw new Error(`tarball entry leaves the package: ${fullPath}`);
127
+ const path = parts.slice(1).join('/');
128
+ if (path === '')
129
+ continue;
130
+ files.push({ path, content: new Uint8Array(content), executable: (readOctal(block, 100, 8) & 0o111) !== 0 });
131
+ }
132
+ return files;
133
+ }
134
+ /** The Subresource Integrity form npm and pnpm record: `sha512-<base64>`. */
135
+ export function integrityOf(bytes) {
136
+ return `sha512-${createHash('sha512').update(bytes).digest('base64')}`;
137
+ }
138
+ //# sourceMappingURL=tarball.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tarball.js","sourceRoot":"","sources":["../src/tarball.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export interface Version {
2
+ readonly major: number;
3
+ readonly minor: number;
4
+ readonly patch: number;
5
+ readonly prerelease: readonly string[];
6
+ readonly text: string;
7
+ }
8
+ export declare function parseVersion(text: string): Version | undefined;
9
+ /** Semver precedence: negative when `a` is older than `b`. Build metadata is ignored, as semver says. */
10
+ export declare function compareVersions(a: Version, b: Version): number;
11
+ /** The newest of some version strings; ones that are not semver are ignored. */
12
+ export declare function newestVersion(texts: Iterable<string>): string | undefined;
13
+ export type VersionBump = 'patch' | 'minor' | 'major';
14
+ /** The next version for a fix (`patch`), a feature (`minor`) or a breaking change (`major`). A prerelease is released as itself on `patch`. */
15
+ export declare function bumpVersion(text: string, bump: VersionBump): string;
16
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAU9D;AAUD,yGAAyG;AACzG,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,MAAM,CAc9D;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,SAAS,CAOzE;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAA;AAErD,+IAA+I;AAC/I,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAOnE"}
@@ -0,0 +1,69 @@
1
+ // Semantic versions, only as far as the hub needs them: validating a version, ordering versions to find the newest, and bumping one for a fix.
2
+ const SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/u;
3
+ export function parseVersion(text) {
4
+ const match = SEMVER.exec(text.trim());
5
+ if (match === null)
6
+ return undefined;
7
+ return {
8
+ major: Number(match[1]),
9
+ minor: Number(match[2]),
10
+ patch: Number(match[3]),
11
+ prerelease: match[4] === undefined ? [] : match[4].split('.'),
12
+ text: text.trim(),
13
+ };
14
+ }
15
+ function compareIdentifiers(a, b) {
16
+ const numeric = /^\d+$/u;
17
+ if (numeric.test(a) && numeric.test(b))
18
+ return Number(a) - Number(b);
19
+ if (numeric.test(a))
20
+ return -1;
21
+ if (numeric.test(b))
22
+ return 1;
23
+ return a < b ? -1 : a > b ? 1 : 0;
24
+ }
25
+ /** Semver precedence: negative when `a` is older than `b`. Build metadata is ignored, as semver says. */
26
+ export function compareVersions(a, b) {
27
+ for (const key of ['major', 'minor', 'patch']) {
28
+ if (a[key] !== b[key])
29
+ return a[key] - b[key];
30
+ }
31
+ if (a.prerelease.length === 0 || b.prerelease.length === 0)
32
+ return b.prerelease.length - a.prerelease.length;
33
+ for (let index = 0; index < Math.max(a.prerelease.length, b.prerelease.length); index += 1) {
34
+ const left = a.prerelease[index];
35
+ const right = b.prerelease[index];
36
+ if (left === undefined)
37
+ return -1;
38
+ if (right === undefined)
39
+ return 1;
40
+ const order = compareIdentifiers(left, right);
41
+ if (order !== 0)
42
+ return order;
43
+ }
44
+ return 0;
45
+ }
46
+ /** The newest of some version strings; ones that are not semver are ignored. */
47
+ export function newestVersion(texts) {
48
+ let newest;
49
+ for (const text of texts) {
50
+ const version = parseVersion(text);
51
+ if (version !== undefined && (newest === undefined || compareVersions(version, newest) > 0))
52
+ newest = version;
53
+ }
54
+ return newest?.text;
55
+ }
56
+ /** The next version for a fix (`patch`), a feature (`minor`) or a breaking change (`major`). A prerelease is released as itself on `patch`. */
57
+ export function bumpVersion(text, bump) {
58
+ const version = parseVersion(text);
59
+ if (version === undefined)
60
+ throw new Error(`${JSON.stringify(text)} is not a semantic version`);
61
+ if (bump === 'major')
62
+ return `${version.major + 1}.0.0`;
63
+ if (bump === 'minor')
64
+ return `${version.major}.${version.minor + 1}.0`;
65
+ if (version.prerelease.length > 0)
66
+ return `${version.major}.${version.minor}.${version.patch}`;
67
+ return `${version.major}.${version.minor}.${version.patch + 1}`;
68
+ }
69
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,47 @@
1
1
  {
2
2
  "name": "@webboxes/plugin-hub",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Saving, sharing and pulling Cordis plugins: dotted plugin names, the source argument, deterministic packing with sha512, the local plugin store, and the pull pipeline. Host-independent: no Cordis, DSH or surface dependencies.",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public",
8
+ "registry": "https://registry.npmjs.org"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/acryldev/acryl.git",
13
+ "directory": "runtime/plugin-hub"
14
+ },
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./package.json": "./package.json"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "engines": {
31
+ "node": "^22.19.0 || >=24.0.0"
32
+ },
33
+ "dependencies": {
34
+ "@webboxes/app-persistence": "0.1.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "22.20.1",
38
+ "typescript": "6.0.3",
39
+ "vitest": "4.1.11"
40
+ },
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.json",
43
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json",
44
+ "test": "vitest run",
45
+ "check": "pnpm run build && pnpm run typecheck && pnpm run test"
46
+ }
6
47
  }