aontu 0.63.0 → 0.65.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 +8 -8
- package/dist/agentsmd.js +3 -3
- package/dist/agentsmd.js.map +1 -1
- package/dist/aontu.d.ts +3 -3
- package/dist/aontu.js +4 -6
- package/dist/aontu.js.map +1 -1
- package/dist/aontumodel.js +6 -12
- package/dist/aontumodel.js.map +1 -1
- package/dist/casing.d.ts +5 -0
- package/dist/casing.js +90 -0
- package/dist/casing.js.map +1 -0
- package/dist/cli.d.ts +16 -3
- package/dist/cli.js +653 -455
- package/dist/cli.js.map +1 -1
- package/dist/compat.d.ts +7 -0
- package/dist/compat.js +113 -0
- package/dist/compat.js.map +1 -0
- package/dist/helpdoc.js +5 -5
- package/dist/helpdoc.js.map +1 -1
- package/dist/hints.js +14 -5
- package/dist/hints.js.map +1 -1
- package/dist/lang.d.ts +1 -0
- package/dist/lang.js +8 -0
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/mcp.js +0 -36
- package/dist/mcp.js.map +1 -1
- package/dist/mod.d.ts +18 -3
- package/dist/mod.js +117 -46
- package/dist/mod.js.map +1 -1
- package/dist/pkg-net.d.ts +227 -0
- package/dist/pkg-net.js +1598 -0
- package/dist/pkg-net.js.map +1 -0
- package/dist/pkg-zip.d.ts +8 -0
- package/dist/pkg-zip.js +196 -0
- package/dist/pkg-zip.js.map +1 -0
- package/dist/pkg.d.ts +159 -0
- package/dist/pkg.js +767 -0
- package/dist/pkg.js.map +1 -0
- package/dist/profile.d.ts +9 -0
- package/dist/profile.js +28 -0
- package/dist/profile.js.map +1 -0
- package/dist/report-sarif.js +1 -1
- package/dist/report-sarif.js.map +1 -1
- package/dist/subsume.d.ts +3 -1
- package/dist/subsume.js +2 -0
- package/dist/subsume.js.map +1 -1
- package/dist/trace.d.ts +21 -0
- package/dist/trace.js +107 -0
- package/dist/trace.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/unify.js +1 -6
- package/dist/unify.js.map +1 -1
- package/dist/val/CmpFuncVal.d.ts +2 -0
- package/dist/val/CmpFuncVal.js +47 -13
- package/dist/val/CmpFuncVal.js.map +1 -1
- package/dist/val/NomFuncVal.js +11 -13
- package/dist/val/NomFuncVal.js.map +1 -1
- package/dist/val/ReferFuncVal.js +3 -0
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +3 -3
- package/skill/error-codes.md +4 -4
- package/skill/examples.md +4 -4
- package/skill/grammar-card.md +1 -1
- package/skill/init/check.sh +2 -2
- package/skill/tasks.md +14 -12
- package/src/agentsmd.ts +3 -3
- package/src/aontu.ts +3 -8
- package/src/aontumodel.ts +6 -12
- package/src/casing.ts +95 -0
- package/src/cli.ts +758 -495
- package/src/compat.ts +148 -0
- package/src/helpdoc.ts +5 -5
- package/src/hints.ts +14 -7
- package/src/lang.ts +9 -1
- package/src/mcp.ts +0 -39
- package/src/mod.ts +142 -51
- package/src/pkg-net.ts +1945 -0
- package/src/pkg-zip.ts +228 -0
- package/src/pkg.ts +1018 -0
- package/src/profile.ts +42 -0
- package/src/report-sarif.ts +1 -1
- package/src/subsume.ts +2 -2
- package/src/trace.ts +157 -0
- package/src/unify.ts +1 -6
- package/src/val/CmpFuncVal.ts +66 -20
- package/src/val/NomFuncVal.ts +3 -5
- package/src/val/ReferFuncVal.ts +4 -0
- package/dist/lower.d.ts +0 -23
- package/dist/lower.js +0 -528
- package/dist/lower.js.map +0 -1
- package/dist/mod-tool.d.ts +0 -58
- package/dist/mod-tool.js +0 -403
- package/dist/mod-tool.js.map +0 -1
- package/dist/render.d.ts +0 -53
- package/dist/render.js +0 -432
- package/dist/render.js.map +0 -1
- package/src/lower.ts +0 -586
- package/src/mod-tool.ts +0 -561
- package/src/render.ts +0 -580
package/dist/pkg.js
ADDED
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.MANIFEST_SCHEMA = exports.REL_PATH_MAX_ELEMENTS = exports.ARCHIVE_NAMED = exports.ARCHIVE_TEXT_EXT = exports.ARCHIVE_DATA_EXT = exports.ARCHIVE_SOURCE_EXT = exports.LOCK_HEADER = exports.ARCHIVE_LIMITS = exports.VERSION_RE = void 0;
|
|
5
|
+
exports.declaredDeps = declaredDeps;
|
|
6
|
+
exports.versionCompare = versionCompare;
|
|
7
|
+
exports.usableKey = usableKey;
|
|
8
|
+
exports.storeDir = storeDir;
|
|
9
|
+
exports.downloadedCanon = downloadedCanon;
|
|
10
|
+
exports.readLock = readLock;
|
|
11
|
+
exports.lockText = lockText;
|
|
12
|
+
exports.writeLock = writeLock;
|
|
13
|
+
exports.packageSelf = packageSelf;
|
|
14
|
+
exports.archiveAdmits = archiveAdmits;
|
|
15
|
+
exports.archiveOf = archiveOf;
|
|
16
|
+
exports.storedManifest = storedManifest;
|
|
17
|
+
exports.pkgTidy = pkgTidy;
|
|
18
|
+
exports.pkgResolve = pkgResolve;
|
|
19
|
+
exports.targetOf = targetOf;
|
|
20
|
+
exports.pkgVerify = pkgVerify;
|
|
21
|
+
exports.pkgVendor = pkgVendor;
|
|
22
|
+
exports.vendorCopy = vendorCopy;
|
|
23
|
+
exports.copyTree = copyTree;
|
|
24
|
+
exports.archiveOverCaps = archiveOverCaps;
|
|
25
|
+
exports.relPathError = relPathError;
|
|
26
|
+
exports.pkgRefreeze = pkgRefreeze;
|
|
27
|
+
exports.pkgTree = pkgTree;
|
|
28
|
+
exports.pkgManifest = pkgManifest;
|
|
29
|
+
exports.manifestText = manifestText;
|
|
30
|
+
const node_fs_1 = require("node:fs");
|
|
31
|
+
const node_path_1 = require("node:path");
|
|
32
|
+
const mod_1 = require("./mod");
|
|
33
|
+
const pkg_zip_1 = require("./pkg-zip");
|
|
34
|
+
const subsume_1 = require("./subsume");
|
|
35
|
+
const compat_1 = require("./compat");
|
|
36
|
+
const lang_1 = require("./lang");
|
|
37
|
+
exports.VERSION_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
38
|
+
// params.archive: what a consumer refuses to unpack, and so what a
|
|
39
|
+
// publisher refuses to mint. A record, so a test can lower them.
|
|
40
|
+
exports.ARCHIVE_LIMITS = {
|
|
41
|
+
bytes: 16777216, unpacked: 67108864, files: 4096, fileBytes: 8388608,
|
|
42
|
+
};
|
|
43
|
+
// The `dep` block a package file declares: key -> minimum version, and
|
|
44
|
+
// for an alias the package it names.
|
|
45
|
+
function declaredDeps(file, options) {
|
|
46
|
+
if (!(0, node_fs_1.existsSync)(file)) {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
const gen = options.eval((0, node_fs_1.readFileSync)(file, 'utf8'), file).gen;
|
|
50
|
+
const dep = gen?.dep;
|
|
51
|
+
if (null == dep || 'object' !== typeof dep) {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
const out = {};
|
|
55
|
+
for (const key of Object.keys(dep)) {
|
|
56
|
+
const v = dep[key]?.v;
|
|
57
|
+
if ('string' === typeof v && '' !== v) {
|
|
58
|
+
const pkg = dep[key]?.pkg;
|
|
59
|
+
out[key] = { v, ...('string' === typeof pkg && '' !== pkg ? { pkg } : {}) };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function versionCompare(a, b) {
|
|
65
|
+
const ap = a.split('.');
|
|
66
|
+
const bp = b.split('.');
|
|
67
|
+
for (let i = 0; i < Math.max(ap.length, bp.length); i++) {
|
|
68
|
+
// A part the shorter version does not have is ZERO, so `1.2` and
|
|
69
|
+
// `1.2.0` are the same version -- which is what everyone means by
|
|
70
|
+
// them, and what a lockfile rewritten from either must agree on.
|
|
71
|
+
const x = ap[i] ?? '0';
|
|
72
|
+
const y = bp[i] ?? '0';
|
|
73
|
+
if (x === y) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const xn = /^\d+$/.test(x);
|
|
77
|
+
const yn = /^\d+$/.test(y);
|
|
78
|
+
if (xn && yn) {
|
|
79
|
+
const xs = x.replace(/^0+(?=\d)/, '');
|
|
80
|
+
const ys = y.replace(/^0+(?=\d)/, '');
|
|
81
|
+
if (xs === ys) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
return xs.length !== ys.length ? (xs.length < ys.length ? -1 : 1) : (xs < ys ? -1 : 1);
|
|
85
|
+
}
|
|
86
|
+
if (xn !== yn) {
|
|
87
|
+
return xn ? -1 : 1;
|
|
88
|
+
}
|
|
89
|
+
return x < y ? -1 : 1;
|
|
90
|
+
}
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
// A last element with an extension the include table knows is a
|
|
94
|
+
// local file to the resolver, whatever declares it, so it names no
|
|
95
|
+
// package.
|
|
96
|
+
function usableKey(key) {
|
|
97
|
+
const ref = (0, mod_1.parseModuleRef)(key);
|
|
98
|
+
if (undefined === ref || ref.path !== key) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
if ((0, mod_1.isAlias)(key)) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
const ext = (0, mod_1.localFileExt)(key);
|
|
105
|
+
return undefined === (0, mod_1.validateModulePath)(key) &&
|
|
106
|
+
(undefined === ext || undefined === (0, lang_1.includeFormat)(ext));
|
|
107
|
+
}
|
|
108
|
+
// The directory a package is in, in the local stores: the project's
|
|
109
|
+
// vendor tree first, then the cache under the hash the lockfile pins,
|
|
110
|
+
// then the cache under the hash the repository's manifest for that
|
|
111
|
+
// version pins, which the consumer's own pin need not equal.
|
|
112
|
+
function storeDir(root, key, canon, pkg, options, v) {
|
|
113
|
+
const stores = [(0, mod_1.moduleDir)((0, node_path_1.join)(root, mod_1.META_DIR, mod_1.VENDOR_DIR), key)];
|
|
114
|
+
if (null != options.cache && '' !== pkg) {
|
|
115
|
+
if ('' !== canon) {
|
|
116
|
+
stores.push((0, mod_1.cacheStoreDir)(options.cache, canon, pkg));
|
|
117
|
+
}
|
|
118
|
+
const served = null == v ? undefined : downloadedCanon(options.cache, pkg, v);
|
|
119
|
+
if (undefined !== served && served !== canon) {
|
|
120
|
+
stores.push((0, mod_1.cacheStoreDir)(options.cache, served, pkg));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return stores.find((d) => (0, node_fs_1.existsSync)((0, node_path_1.join)(d, mod_1.PKG_FILE)));
|
|
124
|
+
}
|
|
125
|
+
// The canon the repository's manifest pins for a version this client
|
|
126
|
+
// downloaded, or undefined where none was.
|
|
127
|
+
function downloadedCanon(cache, pkg, v) {
|
|
128
|
+
const file = (0, node_path_1.join)((0, mod_1.cacheDownloadDir)(cache, pkg), v + '.manifest');
|
|
129
|
+
if (!(0, node_fs_1.existsSync)(file)) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
let doc;
|
|
133
|
+
try {
|
|
134
|
+
doc = JSON.parse((0, node_fs_1.readFileSync)(file, 'utf8'));
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
const canon = doc?.modules?.[0]?.canon;
|
|
140
|
+
return 'string' === typeof canon ? canon : undefined;
|
|
141
|
+
}
|
|
142
|
+
function readLock(root) {
|
|
143
|
+
const file = (0, node_path_1.join)(root, mod_1.META_DIR, mod_1.LOCK_FILE);
|
|
144
|
+
if (!(0, node_fs_1.existsSync)(file)) {
|
|
145
|
+
return {};
|
|
146
|
+
}
|
|
147
|
+
let lock;
|
|
148
|
+
try {
|
|
149
|
+
lock = JSON.parse((0, mod_1.lockJson)((0, node_fs_1.readFileSync)(file, 'utf8')));
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return {};
|
|
153
|
+
}
|
|
154
|
+
const out = {};
|
|
155
|
+
for (const key of Object.keys(lock?.lock ?? {})) {
|
|
156
|
+
const e = lock.lock[key];
|
|
157
|
+
const str = (k) => 'string' === typeof e?.[k] ? e[k] : '';
|
|
158
|
+
out[key] = {
|
|
159
|
+
key,
|
|
160
|
+
v: str('v'),
|
|
161
|
+
canon: str('canon'),
|
|
162
|
+
archive: str('archive'),
|
|
163
|
+
...('' === str('manifest') ? {} : { manifest: str('manifest') }),
|
|
164
|
+
...('' === str('pkg') ? {} : { pkg: str('pkg') }),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
function lockText(entries, options) {
|
|
170
|
+
const parts = entries.map((e) => JSON.stringify(e.key) + ':{' +
|
|
171
|
+
'"archive":' + JSON.stringify(e.archive) + ',' +
|
|
172
|
+
'"canon":' + JSON.stringify(e.canon) + ',' +
|
|
173
|
+
(null == e.manifest ? '' : '"manifest":' + JSON.stringify(e.manifest) + ',') +
|
|
174
|
+
(null == e.pkg ? '' : '"pkg":' + JSON.stringify(e.pkg) + ',') +
|
|
175
|
+
'"v":' + JSON.stringify(e.v) + '}');
|
|
176
|
+
return options.eval('{"lock":{' + parts.join(',') + '}}', mod_1.LOCK_FILE).canon;
|
|
177
|
+
}
|
|
178
|
+
// The generated-file header. A lockfile is machine-written, and the
|
|
179
|
+
// file says so where an editor will see it.
|
|
180
|
+
exports.LOCK_HEADER = '# pkg-lock.aon (generated by `aontu sync`; do not edit)\n';
|
|
181
|
+
function writeLock(root, entries, options) {
|
|
182
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.join)(root, mod_1.META_DIR), { recursive: true });
|
|
183
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(root, mod_1.META_DIR, mod_1.LOCK_FILE), exports.LOCK_HEADER + lockText(entries, options) + '\n');
|
|
184
|
+
}
|
|
185
|
+
function packageSelf(dir, options) {
|
|
186
|
+
const file = (0, node_path_1.join)(dir, mod_1.PKG_FILE);
|
|
187
|
+
const self = {
|
|
188
|
+
path: '', version: '', main: 'main.aon', publish: 'private', retract: [],
|
|
189
|
+
};
|
|
190
|
+
if (!(0, node_fs_1.existsSync)(file)) {
|
|
191
|
+
return self;
|
|
192
|
+
}
|
|
193
|
+
const gen = options.eval((0, node_fs_1.readFileSync)(file, 'utf8'), file).gen;
|
|
194
|
+
const pkg = gen?.pkg;
|
|
195
|
+
const str = (v) => 'string' === typeof v ? v : '';
|
|
196
|
+
self.path = str(pkg?.path);
|
|
197
|
+
self.version = str(pkg?.version);
|
|
198
|
+
self.main = '' === str(pkg?.main) ? 'main.aon' : str(pkg?.main);
|
|
199
|
+
self.publish = 'public' === gen?.publish ? 'public' : 'private';
|
|
200
|
+
if ('' !== str(gen?.moved)) {
|
|
201
|
+
self.moved = str(gen.moved);
|
|
202
|
+
}
|
|
203
|
+
if (Array.isArray(gen?.retract)) {
|
|
204
|
+
self.retract = gen.retract.filter((v) => 'string' === typeof v);
|
|
205
|
+
}
|
|
206
|
+
return self;
|
|
207
|
+
}
|
|
208
|
+
// What a package may contain (REPOSITORY.0.md §9.4): an enumerated
|
|
209
|
+
// allowlist, refused by default. The data half is ADR-012's table.
|
|
210
|
+
exports.ARCHIVE_SOURCE_EXT = ['aon', 'aontu'];
|
|
211
|
+
exports.ARCHIVE_DATA_EXT = [
|
|
212
|
+
'json', 'jsonld', 'jsonc', 'json5', 'jsonic', 'jsc', 'toml', 'yaml', 'yml',
|
|
213
|
+
'ini',
|
|
214
|
+
];
|
|
215
|
+
exports.ARCHIVE_TEXT_EXT = ['md', 'txt'];
|
|
216
|
+
exports.ARCHIVE_NAMED = ['LICENSE', 'NOTICE'];
|
|
217
|
+
const ADMITTED_EXT = new Set([
|
|
218
|
+
...exports.ARCHIVE_SOURCE_EXT, ...exports.ARCHIVE_DATA_EXT, ...exports.ARCHIVE_TEXT_EXT
|
|
219
|
+
]);
|
|
220
|
+
function archiveAdmits(rel) {
|
|
221
|
+
const elems = rel.split('/');
|
|
222
|
+
if (elems.some((e) => e.startsWith('.'))) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
const name = elems[elems.length - 1];
|
|
226
|
+
if (exports.ARCHIVE_NAMED.includes(name)) {
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
const m = /^[^.].*\.([^.]+)$/.exec(name);
|
|
230
|
+
return null != m && ADMITTED_EXT.has(m[1].toLowerCase());
|
|
231
|
+
}
|
|
232
|
+
function walkTree(dir, prefix, out, forbidden) {
|
|
233
|
+
for (const name of (0, node_fs_1.readdirSync)(dir).sort(pkg_zip_1.cmpBytes)) {
|
|
234
|
+
if ('' === prefix && mod_1.META_DIR === name) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const full = (0, node_path_1.join)(dir, name);
|
|
238
|
+
const rel = '' === prefix ? name : prefix + '/' + name;
|
|
239
|
+
const st = (0, node_fs_1.lstatSync)(full);
|
|
240
|
+
if (st.isSymbolicLink()) {
|
|
241
|
+
forbidden.push(rel);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (st.isDirectory()) {
|
|
245
|
+
if (name.startsWith('.')) {
|
|
246
|
+
forbidden.push(rel + '/');
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
walkTree(full, rel, out, forbidden);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (!st.isFile() || 0 !== (st.mode & 0o111) || !archiveAdmits(rel)) {
|
|
253
|
+
forbidden.push(rel);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
out.push({ path: rel, data: new Uint8Array((0, node_fs_1.readFileSync)(full)) });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// The canonical archive of a tree, and every file's own digest.
|
|
260
|
+
function archiveOf(dir) {
|
|
261
|
+
const entries = [];
|
|
262
|
+
const forbidden = [];
|
|
263
|
+
walkTree(dir, '', entries, forbidden);
|
|
264
|
+
const zip = (0, pkg_zip_1.zipCanonical)(entries);
|
|
265
|
+
return {
|
|
266
|
+
zip,
|
|
267
|
+
digest: (0, pkg_zip_1.sha256Hex)(zip),
|
|
268
|
+
size: zip.length,
|
|
269
|
+
files: entries.map((e) => ({
|
|
270
|
+
path: e.path, digest: (0, pkg_zip_1.sha256Hex)(e.data), size: e.data.length,
|
|
271
|
+
})),
|
|
272
|
+
forbidden: forbidden.sort(pkg_zip_1.cmpBytes),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
// The pin a locked tree carries beside it, when it was acquired from a
|
|
276
|
+
// repository: the served manifest, verbatim.
|
|
277
|
+
function storedManifest(dir) {
|
|
278
|
+
const file = (0, node_path_1.join)(dir, mod_1.META_DIR, 'manifest.aon');
|
|
279
|
+
if (!(0, node_fs_1.existsSync)(file)) {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
const bytes = new Uint8Array((0, node_fs_1.readFileSync)(file));
|
|
283
|
+
let doc;
|
|
284
|
+
try {
|
|
285
|
+
doc = JSON.parse((0, mod_1.lockJson)(new TextDecoder().decode(bytes)));
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
return { digest: (0, pkg_zip_1.sha256Hex)(bytes), files: [] };
|
|
289
|
+
}
|
|
290
|
+
const files = Array.isArray(doc?.archive?.files) ? doc.archive.files : [];
|
|
291
|
+
return { digest: (0, pkg_zip_1.sha256Hex)(bytes), files };
|
|
292
|
+
}
|
|
293
|
+
function mainOf(dir, options) {
|
|
294
|
+
return packageSelf(dir, options).main;
|
|
295
|
+
}
|
|
296
|
+
// The lock entry a store tree yields for a key: its version as
|
|
297
|
+
// selected, its canon-hash, its archive digest, and the manifest digest
|
|
298
|
+
// where a manifest is kept beside it.
|
|
299
|
+
function pinTree(key, dir, v, pkg, options) {
|
|
300
|
+
const main = (0, node_path_1.join)(dir, mainOf(dir, options));
|
|
301
|
+
const got = (0, node_fs_1.existsSync)(main) ?
|
|
302
|
+
options.eval((0, node_fs_1.readFileSync)(main, 'utf8'), main) : undefined;
|
|
303
|
+
if (null != got && !got.ok) {
|
|
304
|
+
return { unevaluable: true, forbidden: [] };
|
|
305
|
+
}
|
|
306
|
+
const archive = archiveOf(dir);
|
|
307
|
+
const manifest = storedManifest(dir);
|
|
308
|
+
return {
|
|
309
|
+
entry: {
|
|
310
|
+
key,
|
|
311
|
+
v,
|
|
312
|
+
canon: null == got ? '' : got.hash,
|
|
313
|
+
archive: archive.digest,
|
|
314
|
+
...(null == manifest ? {} : { manifest: manifest.digest }),
|
|
315
|
+
...(null == pkg ? {} : { pkg }),
|
|
316
|
+
},
|
|
317
|
+
forbidden: archive.forbidden.map((f) => key + ': ' + f),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
// `aontu pkg tidy`: resolve the closure by MVS and rewrite the lockfile.
|
|
321
|
+
function pkgTidy(root, options) {
|
|
322
|
+
const report = pkgResolve(root, options);
|
|
323
|
+
if ('ok' === report.verdict) {
|
|
324
|
+
writeLock(root, report.lock, options);
|
|
325
|
+
}
|
|
326
|
+
return report;
|
|
327
|
+
}
|
|
328
|
+
// The resolution alone, for a caller that decides whether to write.
|
|
329
|
+
function pkgResolve(root, options) {
|
|
330
|
+
const previous = readLock(root);
|
|
331
|
+
const selected = {};
|
|
332
|
+
const missing = [];
|
|
333
|
+
let frontier = declaredDeps((0, node_path_1.join)(root, mod_1.PKG_FILE), options);
|
|
334
|
+
for (; 0 < Object.keys(frontier).length;) {
|
|
335
|
+
const next = {};
|
|
336
|
+
for (const key of Object.keys(frontier)) {
|
|
337
|
+
const want = frontier[key];
|
|
338
|
+
const have = selected[key];
|
|
339
|
+
if (null != have && 0 <= versionCompare(have.v, want.v)) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
selected[key] = { ...have, ...want };
|
|
343
|
+
if (!usableKey(key)) {
|
|
344
|
+
// A key this tooling cannot act on names nothing any store
|
|
345
|
+
// can hold: the same answer as an absent package.
|
|
346
|
+
missing.push(key);
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const dir = storeDir(root, key, previous[key]?.canon ?? '', targetOf(key, selected[key], previous[key]), options, selected[key].v);
|
|
350
|
+
if (undefined === dir) {
|
|
351
|
+
missing.push(key);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const deps = declaredDeps((0, node_path_1.join)(dir, mod_1.PKG_FILE), options);
|
|
355
|
+
for (const dk of Object.keys(deps)) {
|
|
356
|
+
const bid = next[dk];
|
|
357
|
+
if (null == bid || 0 > versionCompare(bid.v, deps[dk].v)) {
|
|
358
|
+
next[dk] = { ...bid, ...deps[dk] };
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
frontier = next;
|
|
363
|
+
}
|
|
364
|
+
const lock = [];
|
|
365
|
+
const unevaluable = [];
|
|
366
|
+
const forbidden = [];
|
|
367
|
+
for (const key of Object.keys(selected).sort(pkg_zip_1.cmpBytes)) {
|
|
368
|
+
if (missing.includes(key)) {
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
const pkg = targetOf(key, selected[key], previous[key]);
|
|
372
|
+
const dir = storeDir(root, key, previous[key]?.canon ?? '', pkg, options, selected[key].v);
|
|
373
|
+
const pinned = pinTree(key, dir, selected[key].v, (0, mod_1.isAlias)(key) ? pkg : undefined, options);
|
|
374
|
+
forbidden.push(...pinned.forbidden);
|
|
375
|
+
if (pinned.unevaluable) {
|
|
376
|
+
unevaluable.push(key);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
lock.push(pinned.entry);
|
|
380
|
+
}
|
|
381
|
+
const uniqueMissing = [...new Set(missing)].sort(pkg_zip_1.cmpBytes);
|
|
382
|
+
const uniqueUnevaluable = [...new Set(unevaluable)].sort(pkg_zip_1.cmpBytes);
|
|
383
|
+
const held = 0 === uniqueMissing.length && 0 === uniqueUnevaluable.length &&
|
|
384
|
+
0 === forbidden.length;
|
|
385
|
+
return {
|
|
386
|
+
verdict: held ? 'ok' :
|
|
387
|
+
0 < uniqueUnevaluable.length || 0 < forbidden.length ? 'error' : 'missing',
|
|
388
|
+
lock,
|
|
389
|
+
missing: uniqueMissing,
|
|
390
|
+
unevaluable: uniqueUnevaluable,
|
|
391
|
+
forbidden: forbidden.sort(pkg_zip_1.cmpBytes),
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
// The package an alias key names: from the declaration, else from the
|
|
395
|
+
// previous lock; a package path names itself.
|
|
396
|
+
function targetOf(key, dep, prev) {
|
|
397
|
+
if (!(0, mod_1.isAlias)(key)) {
|
|
398
|
+
return key;
|
|
399
|
+
}
|
|
400
|
+
return dep?.pkg ?? prev?.pkg ?? '';
|
|
401
|
+
}
|
|
402
|
+
function pkgVerify(root, options) {
|
|
403
|
+
const locked = readLock(root);
|
|
404
|
+
const verified = [];
|
|
405
|
+
const mismatched = [];
|
|
406
|
+
const missing = [];
|
|
407
|
+
const declared = declaredDeps((0, node_path_1.join)(root, mod_1.PKG_FILE), options);
|
|
408
|
+
const unlocked = Object.keys(declared)
|
|
409
|
+
.filter((key) => null == locked[key]).sort(pkg_zip_1.cmpBytes);
|
|
410
|
+
for (const key of Object.keys(locked).sort(pkg_zip_1.cmpBytes)) {
|
|
411
|
+
const entry = locked[key];
|
|
412
|
+
if (!usableKey(key)) {
|
|
413
|
+
missing.push(key);
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
const dir = storeDir(root, key, entry.canon, entry.pkg ?? key, options, entry.v);
|
|
417
|
+
if (undefined === dir) {
|
|
418
|
+
missing.push(key);
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
const main = (0, node_path_1.join)(dir, mainOf(dir, options));
|
|
422
|
+
if (!(0, node_fs_1.existsSync)(main)) {
|
|
423
|
+
missing.push(key);
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
// BYTES BEFORE MEANING (ADR-019): the archive digest and the
|
|
427
|
+
// manifest's file list first, on fixed-size reads, then the one
|
|
428
|
+
// evaluation.
|
|
429
|
+
const before = mismatched.length;
|
|
430
|
+
const archive = archiveOf(dir);
|
|
431
|
+
for (const f of archive.forbidden) {
|
|
432
|
+
mismatched.push({ key, pin: 'archive', want: entry.archive, got: 'forbidden: ' + f });
|
|
433
|
+
}
|
|
434
|
+
if (0 === archive.forbidden.length && entry.archive !== archive.digest) {
|
|
435
|
+
mismatched.push({ key, pin: 'archive', want: entry.archive, got: archive.digest });
|
|
436
|
+
}
|
|
437
|
+
const manifest = storedManifest(dir);
|
|
438
|
+
if (null == manifest) {
|
|
439
|
+
if (null != entry.manifest) {
|
|
440
|
+
mismatched.push({ key, pin: 'manifest', want: entry.manifest, got: '' });
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
if (null != entry.manifest && entry.manifest !== manifest.digest) {
|
|
445
|
+
mismatched.push({ key, pin: 'manifest', want: entry.manifest, got: manifest.digest });
|
|
446
|
+
}
|
|
447
|
+
const listed = new Map(manifest.files.map((f) => [f.path, f.digest]));
|
|
448
|
+
for (const f of archive.files) {
|
|
449
|
+
if (listed.get(f.path) !== f.digest) {
|
|
450
|
+
mismatched.push({ key, pin: 'manifest', want: listed.get(f.path) ?? '',
|
|
451
|
+
got: f.path + ' ' + f.digest });
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (before !== mismatched.length) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
const got = options.eval((0, node_fs_1.readFileSync)(main, 'utf8'), main);
|
|
459
|
+
const want = entry.canon;
|
|
460
|
+
if (got.ok && want === got.hash) {
|
|
461
|
+
verified.push(key);
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
mismatched.push({ key, pin: 'canon', want, got: got.ok ? got.hash : '' });
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
verdict: 0 < mismatched.length ? 'mismatch' :
|
|
468
|
+
0 < unlocked.length ? 'unlocked' :
|
|
469
|
+
0 < missing.length ? 'missing' : 'ok',
|
|
470
|
+
verified,
|
|
471
|
+
mismatched,
|
|
472
|
+
unlocked,
|
|
473
|
+
missing: missing.sort(pkg_zip_1.cmpBytes),
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
// `aontu pkg vendor`: materialise the locked closure into `aontu_meta/vendor/`.
|
|
477
|
+
function pkgVendor(root, options) {
|
|
478
|
+
const locked = readLock(root);
|
|
479
|
+
const vendored = [];
|
|
480
|
+
const missing = [];
|
|
481
|
+
const vendorRoot = (0, node_path_1.join)(root, mod_1.META_DIR, mod_1.VENDOR_DIR);
|
|
482
|
+
for (const key of Object.keys(locked).sort(pkg_zip_1.cmpBytes)) {
|
|
483
|
+
const entry = locked[key];
|
|
484
|
+
if (!usableKey(key)) {
|
|
485
|
+
missing.push(key);
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
const from = storeDir(root, key, entry.canon, entry.pkg ?? key, options, entry.v);
|
|
489
|
+
if (undefined === from) {
|
|
490
|
+
missing.push(key);
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
const to = (0, mod_1.moduleDir)(vendorRoot, key);
|
|
494
|
+
if (from !== to) {
|
|
495
|
+
vendorCopy(from, to);
|
|
496
|
+
}
|
|
497
|
+
vendored.push(key);
|
|
498
|
+
}
|
|
499
|
+
return {
|
|
500
|
+
verdict: 0 === missing.length ? 'ok' : 'missing',
|
|
501
|
+
vendored,
|
|
502
|
+
missing: missing.sort(pkg_zip_1.cmpBytes),
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
// A store tree into the vendor tree: the whole directory, less the
|
|
506
|
+
// store's own lock, so the copy resolves against the consumer's.
|
|
507
|
+
function vendorCopy(from, to) {
|
|
508
|
+
(0, node_fs_1.rmSync)(to, { recursive: true, force: true });
|
|
509
|
+
copyTree(from, to);
|
|
510
|
+
const lock = (0, node_path_1.join)(to, mod_1.META_DIR, mod_1.LOCK_FILE);
|
|
511
|
+
if ((0, node_fs_1.existsSync)(lock)) {
|
|
512
|
+
(0, node_fs_1.unlinkSync)(lock);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
// A whole package directory, copied as real files: the vendor tree is
|
|
516
|
+
// committed, and a link into a shared store is corruption waiting for
|
|
517
|
+
// an edit.
|
|
518
|
+
function copyTree(from, to) {
|
|
519
|
+
(0, node_fs_1.mkdirSync)(to, { recursive: true });
|
|
520
|
+
for (const name of (0, node_fs_1.readdirSync)(from).sort(pkg_zip_1.cmpBytes)) {
|
|
521
|
+
const src = (0, node_path_1.join)(from, name);
|
|
522
|
+
const dst = (0, node_path_1.join)(to, name);
|
|
523
|
+
if ((0, node_fs_1.statSync)(src).isDirectory()) {
|
|
524
|
+
copyTree(src, dst);
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(dst), { recursive: true });
|
|
528
|
+
(0, node_fs_1.copyFileSync)(src, dst);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
// Every consumer refuses an archive past params.archive at acquire, so
|
|
533
|
+
// a publisher refuses to mint one: the reasons, as the forbidden list.
|
|
534
|
+
function archiveOverCaps(archive) {
|
|
535
|
+
const over = [];
|
|
536
|
+
if (exports.ARCHIVE_LIMITS.bytes < archive.size) {
|
|
537
|
+
over.push('archive: ' + archive.size + ' bytes, over the cap of ' + exports.ARCHIVE_LIMITS.bytes);
|
|
538
|
+
}
|
|
539
|
+
if (exports.ARCHIVE_LIMITS.files < archive.files.length) {
|
|
540
|
+
over.push('archive: ' + archive.files.length + ' files, over the cap of ' + exports.ARCHIVE_LIMITS.files);
|
|
541
|
+
}
|
|
542
|
+
let total = 0;
|
|
543
|
+
for (const f of archive.files) {
|
|
544
|
+
total += f.size;
|
|
545
|
+
if (exports.ARCHIVE_LIMITS.fileBytes < f.size) {
|
|
546
|
+
over.push(f.path + ': ' + f.size + ' bytes, over the cap of ' + exports.ARCHIVE_LIMITS.fileBytes);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (exports.ARCHIVE_LIMITS.unpacked < total) {
|
|
550
|
+
over.push('archive: unpacks to ' + total + ' bytes, over the cap of ' + exports.ARCHIVE_LIMITS.unpacked);
|
|
551
|
+
}
|
|
552
|
+
return over;
|
|
553
|
+
}
|
|
554
|
+
const RESERVED_NAME_RE = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\..*)?$/i;
|
|
555
|
+
exports.REL_PATH_MAX_ELEMENTS = 32;
|
|
556
|
+
// A path inside an archive: forward slashes, the element rules, never
|
|
557
|
+
// absolute, never escaping, never a name one platform reserves.
|
|
558
|
+
function relPathError(p) {
|
|
559
|
+
if ('' === p || 512 < p.length || p.startsWith('/') || p.endsWith('/')) {
|
|
560
|
+
return 'an entry path is empty, absolute or a directory';
|
|
561
|
+
}
|
|
562
|
+
const elements = p.split('/');
|
|
563
|
+
if (exports.REL_PATH_MAX_ELEMENTS < elements.length) {
|
|
564
|
+
return 'an entry path has more than ' + exports.REL_PATH_MAX_ELEMENTS + ' elements';
|
|
565
|
+
}
|
|
566
|
+
for (const e of elements) {
|
|
567
|
+
if ('' === e || '.' === e || '..' === e || e.startsWith('.') || e.endsWith('.')) {
|
|
568
|
+
return 'an entry path element is empty or begins or ends with a dot';
|
|
569
|
+
}
|
|
570
|
+
if (!/^[A-Za-z0-9._-]+$/.test(e)) {
|
|
571
|
+
return 'an entry path element is outside the alphabet';
|
|
572
|
+
}
|
|
573
|
+
if (RESERVED_NAME_RE.test(e)) {
|
|
574
|
+
return 'an entry path element is a name a platform reserves';
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return undefined;
|
|
578
|
+
}
|
|
579
|
+
// `aontu pkg refreeze`: recompute every canon pin and nothing else,
|
|
580
|
+
// which is what a canonical-form change in the engine needs.
|
|
581
|
+
function pkgRefreeze(root, options) {
|
|
582
|
+
const locked = readLock(root);
|
|
583
|
+
const repinned = [];
|
|
584
|
+
const unchanged = [];
|
|
585
|
+
const missing = [];
|
|
586
|
+
const unevaluable = [];
|
|
587
|
+
const lock = [];
|
|
588
|
+
for (const key of Object.keys(locked).sort(pkg_zip_1.cmpBytes)) {
|
|
589
|
+
const entry = locked[key];
|
|
590
|
+
const dir = usableKey(key) ?
|
|
591
|
+
storeDir(root, key, entry.canon, entry.pkg ?? key, options, entry.v) : undefined;
|
|
592
|
+
const main = undefined === dir ? undefined : (0, node_path_1.join)(dir, mainOf(dir, options));
|
|
593
|
+
if (undefined === main || !(0, node_fs_1.existsSync)(main)) {
|
|
594
|
+
missing.push(key);
|
|
595
|
+
lock.push(entry);
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
const got = options.eval((0, node_fs_1.readFileSync)(main, 'utf8'), main);
|
|
599
|
+
if (!got.ok) {
|
|
600
|
+
unevaluable.push(key);
|
|
601
|
+
lock.push(entry);
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
if (got.hash === entry.canon) {
|
|
605
|
+
unchanged.push(key);
|
|
606
|
+
lock.push(entry);
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
repinned.push({ key, from: entry.canon, to: got.hash });
|
|
610
|
+
lock.push({ ...entry, canon: got.hash });
|
|
611
|
+
}
|
|
612
|
+
const held = 0 === missing.length && 0 === unevaluable.length;
|
|
613
|
+
if (held && 0 < repinned.length) {
|
|
614
|
+
writeLock(root, lock, options);
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
verdict: held ? 'ok' : 0 < unevaluable.length ? 'error' : 'missing',
|
|
618
|
+
repinned,
|
|
619
|
+
unchanged,
|
|
620
|
+
missing,
|
|
621
|
+
unevaluable,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
// `aontu pkg tree`: the locked closure as a graph, each node's edges
|
|
625
|
+
// read from its own package file in the store.
|
|
626
|
+
function pkgTree(root, options) {
|
|
627
|
+
const locked = readLock(root);
|
|
628
|
+
const self = packageSelf(root, options);
|
|
629
|
+
const nodes = [];
|
|
630
|
+
const missing = [];
|
|
631
|
+
nodes.push({
|
|
632
|
+
key: '' === self.path ? '.' : self.path,
|
|
633
|
+
v: self.version,
|
|
634
|
+
deps: Object.keys(declaredDeps((0, node_path_1.join)(root, mod_1.PKG_FILE), options)).sort(pkg_zip_1.cmpBytes),
|
|
635
|
+
});
|
|
636
|
+
for (const key of Object.keys(locked).sort(pkg_zip_1.cmpBytes)) {
|
|
637
|
+
const entry = locked[key];
|
|
638
|
+
const dir = usableKey(key) ?
|
|
639
|
+
storeDir(root, key, entry.canon, entry.pkg ?? key, options, entry.v) : undefined;
|
|
640
|
+
if (undefined === dir) {
|
|
641
|
+
missing.push(key);
|
|
642
|
+
nodes.push({ key, v: entry.v, deps: [] });
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
nodes.push({
|
|
646
|
+
key, v: entry.v,
|
|
647
|
+
deps: Object.keys(declaredDeps((0, node_path_1.join)(dir, mod_1.PKG_FILE), options)).sort(pkg_zip_1.cmpBytes),
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
return {
|
|
651
|
+
verdict: 0 === missing.length ? 'ok' : 'missing',
|
|
652
|
+
root: nodes[0].key,
|
|
653
|
+
nodes,
|
|
654
|
+
missing,
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
exports.MANIFEST_SCHEMA = 'aontu-package/v1';
|
|
658
|
+
// `aontu pkg manifest`: the manifest a publish would send, and the gate
|
|
659
|
+
// that decides whether it may be.
|
|
660
|
+
function pkgManifest(root, options, against) {
|
|
661
|
+
const self = packageSelf(root, options);
|
|
662
|
+
const missing = [];
|
|
663
|
+
if ('' === self.path) {
|
|
664
|
+
missing.push('pkg.path');
|
|
665
|
+
}
|
|
666
|
+
else if ((0, mod_1.isAlias)(self.path) || !usableKey(self.path)) {
|
|
667
|
+
missing.push('pkg.path (' + self.path + ' is not a package path)');
|
|
668
|
+
}
|
|
669
|
+
if ('' === self.version) {
|
|
670
|
+
missing.push('pkg.version');
|
|
671
|
+
}
|
|
672
|
+
else if (!exports.VERSION_RE.test(self.version)) {
|
|
673
|
+
missing.push('pkg.version (' + self.version + ' is not MAJOR.MINOR.PATCH)');
|
|
674
|
+
}
|
|
675
|
+
const main = (0, node_path_1.join)(root, self.main);
|
|
676
|
+
if (!(0, node_fs_1.existsSync)(main) || undefined !== relPathError(self.main)) {
|
|
677
|
+
missing.push(self.main);
|
|
678
|
+
}
|
|
679
|
+
const refused = (why, forbidden = []) => ({
|
|
680
|
+
verdict: 'error', missing: why.sort(pkg_zip_1.cmpBytes), forbidden, findings: [],
|
|
681
|
+
});
|
|
682
|
+
if (0 < missing.length) {
|
|
683
|
+
return refused(missing);
|
|
684
|
+
}
|
|
685
|
+
const newSrc = (0, node_fs_1.readFileSync)(main, 'utf8');
|
|
686
|
+
const got = options.eval(newSrc, main);
|
|
687
|
+
// Nothing to pin: `tidy` refuses the same way.
|
|
688
|
+
if (!got.ok) {
|
|
689
|
+
return refused([self.main]);
|
|
690
|
+
}
|
|
691
|
+
const archive = archiveOf(root);
|
|
692
|
+
if (0 < archive.forbidden.length) {
|
|
693
|
+
return refused([], archive.forbidden);
|
|
694
|
+
}
|
|
695
|
+
const over = archiveOverCaps(archive);
|
|
696
|
+
if (0 < over.length) {
|
|
697
|
+
return refused([], over);
|
|
698
|
+
}
|
|
699
|
+
const report = {
|
|
700
|
+
verdict: 'ok',
|
|
701
|
+
manifest: {
|
|
702
|
+
schema: exports.MANIFEST_SCHEMA,
|
|
703
|
+
package: self.path,
|
|
704
|
+
version: self.version,
|
|
705
|
+
publish: self.publish,
|
|
706
|
+
archive: {
|
|
707
|
+
format: 'zip', digest: archive.digest, size: archive.size,
|
|
708
|
+
files: archive.files,
|
|
709
|
+
},
|
|
710
|
+
modules: [{ path: self.path, main: self.main, canon: got.hash }],
|
|
711
|
+
deps: declaredDeps((0, node_path_1.join)(root, mod_1.PKG_FILE), options),
|
|
712
|
+
...(0 === self.retract.length ? {} : { retract: self.retract }),
|
|
713
|
+
...(null == self.moved ? {} : { moved: self.moved }),
|
|
714
|
+
},
|
|
715
|
+
missing: [],
|
|
716
|
+
forbidden: [],
|
|
717
|
+
findings: [],
|
|
718
|
+
};
|
|
719
|
+
if (null == against) {
|
|
720
|
+
return report;
|
|
721
|
+
}
|
|
722
|
+
// THE PUBLISH-TIME COMPATIBILITY GATE: G3's subsumption
|
|
723
|
+
// (ts/src/subsume.ts), wired at the one place versions are minted,
|
|
724
|
+
// with no major to bump past it (ADR-022).
|
|
725
|
+
const prior = packageSelf(against, options);
|
|
726
|
+
const priorMain = (0, node_path_1.join)(against, prior.main);
|
|
727
|
+
if (!(0, node_fs_1.existsSync)(priorMain)) {
|
|
728
|
+
report.verdict = 'error';
|
|
729
|
+
report.missing = [prior.main];
|
|
730
|
+
return report;
|
|
731
|
+
}
|
|
732
|
+
const priorSrc = (0, node_fs_1.readFileSync)(priorMain, 'utf8');
|
|
733
|
+
const urls = {
|
|
734
|
+
generalUrl: main,
|
|
735
|
+
specificUrl: priorMain,
|
|
736
|
+
generalPath: main,
|
|
737
|
+
specificPath: priorMain,
|
|
738
|
+
};
|
|
739
|
+
const gate = (0, subsume_1.subsume)(newSrc, priorSrc, urls);
|
|
740
|
+
report.findings = gate.findings;
|
|
741
|
+
report.verdict = MANIFEST_VERDICT[gate.verdict];
|
|
742
|
+
if ('error' === gate.verdict) {
|
|
743
|
+
return report;
|
|
744
|
+
}
|
|
745
|
+
// Admission alone is not compatibility: what the prior version
|
|
746
|
+
// generated, the next must generate, and the same (ts/src/compat.ts).
|
|
747
|
+
const outcome = (0, compat_1.compatOutcome)(newSrc, priorSrc, urls);
|
|
748
|
+
report.findings = report.findings.concat(outcome.findings);
|
|
749
|
+
if ('breaking' === outcome.verdict) {
|
|
750
|
+
report.verdict = 'breaking';
|
|
751
|
+
}
|
|
752
|
+
return report;
|
|
753
|
+
}
|
|
754
|
+
const MANIFEST_VERDICT = {
|
|
755
|
+
subsumes: 'ok',
|
|
756
|
+
does_not_subsume: 'breaking',
|
|
757
|
+
undecided: 'undecided',
|
|
758
|
+
error: 'error',
|
|
759
|
+
};
|
|
760
|
+
// The manifest as the bytes a publish signs and a repository serves:
|
|
761
|
+
// canonical aontu, one line, keys sorted, which for scalar leaves is
|
|
762
|
+
// JSON.
|
|
763
|
+
function manifestText(m, options, extra) {
|
|
764
|
+
const src = JSON.stringify({ ...m, ...(extra ?? {}) });
|
|
765
|
+
return options.eval(src, 'manifest.aon').canon;
|
|
766
|
+
}
|
|
767
|
+
//# sourceMappingURL=pkg.js.map
|