@knitli/openapi-mcp 0.0.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/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +21 -0
- package/README.md +362 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +249 -0
- package/dist/compile.d.ts +14 -0
- package/dist/compile.js +132 -0
- package/dist/compiler.d.ts +13 -0
- package/dist/compiler.js +7 -0
- package/dist/conformance/fixtures.d.ts +24 -0
- package/dist/conformance/fixtures.js +119 -0
- package/dist/conformance/index.d.ts +2 -0
- package/dist/conformance/index.js +2 -0
- package/dist/conformance/runtime-suite.d.ts +31 -0
- package/dist/conformance/runtime-suite.js +343 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/load.d.ts +31 -0
- package/dist/load.js +20 -0
- package/dist/operations.d.ts +27 -0
- package/dist/operations.js +231 -0
- package/dist/permissions.d.ts +31 -0
- package/dist/permissions.js +89 -0
- package/dist/release/compile-release.d.ts +33 -0
- package/dist/release/compile-release.js +1620 -0
- package/dist/release/load-v4.d.ts +29 -0
- package/dist/release/load-v4.js +297 -0
- package/dist/release/manifest-builder.d.ts +19 -0
- package/dist/release/manifest-builder.js +28 -0
- package/dist/release/publish.d.ts +45 -0
- package/dist/release/publish.js +346 -0
- package/dist/release/reference-map.d.ts +53 -0
- package/dist/release/reference-map.js +347 -0
- package/dist/release/schema-v4.d.ts +5 -0
- package/dist/release/schema-v4.js +91 -0
- package/dist/runtime/action-permit.d.ts +11 -0
- package/dist/runtime/action-permit.js +1 -0
- package/dist/runtime/classify.d.ts +16 -0
- package/dist/runtime/classify.js +279 -0
- package/dist/runtime/credential-binding.d.ts +3 -0
- package/dist/runtime/credential-binding.js +101 -0
- package/dist/runtime/digest.d.ts +8 -0
- package/dist/runtime/digest.js +80 -0
- package/dist/runtime/errors.d.ts +14 -0
- package/dist/runtime/errors.js +15 -0
- package/dist/runtime/index.d.ts +20 -0
- package/dist/runtime/index.js +14 -0
- package/dist/runtime/manifest.d.ts +33 -0
- package/dist/runtime/manifest.js +489 -0
- package/dist/runtime/prepared-call.d.ts +39 -0
- package/dist/runtime/prepared-call.js +566 -0
- package/dist/runtime/references.d.ts +7 -0
- package/dist/runtime/references.js +197 -0
- package/dist/runtime/runtime.d.ts +25 -0
- package/dist/runtime/runtime.js +1272 -0
- package/dist/runtime/schema-keywords.d.ts +2 -0
- package/dist/runtime/schema-keywords.js +30 -0
- package/dist/runtime/schema-resolver.d.ts +10 -0
- package/dist/runtime/schema-resolver.js +237 -0
- package/dist/runtime/serialize.d.ts +12 -0
- package/dist/runtime/serialize.js +877 -0
- package/dist/runtime/store.d.ts +52 -0
- package/dist/runtime/store.js +582 -0
- package/dist/runtime/strict-json.d.ts +19 -0
- package/dist/runtime/strict-json.js +539 -0
- package/dist/runtime/types.d.ts +457 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/verify-record.d.ts +6 -0
- package/dist/runtime/verify-record.js +492 -0
- package/dist/runtime/versions.d.ts +63 -0
- package/dist/runtime/versions.js +86 -0
- package/dist/safety.d.ts +28 -0
- package/dist/safety.js +60 -0
- package/dist/schema.d.ts +5 -0
- package/dist/schema.js +52 -0
- package/dist/schemas.d.ts +9 -0
- package/dist/schemas.js +16 -0
- package/dist/sign.d.ts +20 -0
- package/dist/sign.js +55 -0
- package/dist/sqlite/auth.d.ts +33 -0
- package/dist/sqlite/auth.js +973 -0
- package/dist/sqlite/catalog-store.d.ts +20 -0
- package/dist/sqlite/catalog-store.js +370 -0
- package/dist/sqlite/destination-guard.d.ts +24 -0
- package/dist/sqlite/destination-guard.js +140 -0
- package/dist/sqlite/generation-store.d.ts +12 -0
- package/dist/sqlite/generation-store.js +721 -0
- package/dist/sqlite/guarded-fetch.d.ts +24 -0
- package/dist/sqlite/guarded-fetch.js +626 -0
- package/dist/sqlite/index.d.ts +5 -0
- package/dist/sqlite/index.js +5 -0
- package/dist/sqlite/oauth-resource.d.ts +2 -0
- package/dist/sqlite/oauth-resource.js +15 -0
- package/dist/sqlite/release-completion.d.ts +5 -0
- package/dist/sqlite/release-completion.js +80 -0
- package/dist/stdio/action-broker.d.ts +15 -0
- package/dist/stdio/action-broker.js +136 -0
- package/dist/stdio/authorizer.d.ts +30 -0
- package/dist/stdio/authorizer.js +484 -0
- package/dist/stdio/config.d.ts +77 -0
- package/dist/stdio/config.js +144 -0
- package/dist/stdio/exact-policy.d.ts +41 -0
- package/dist/stdio/exact-policy.js +361 -0
- package/dist/stdio/index.d.ts +6 -0
- package/dist/stdio/index.js +148 -0
- package/dist/stdio/render.d.ts +8 -0
- package/dist/stdio/render.js +253 -0
- package/dist/stdio/request-state.d.ts +18 -0
- package/dist/stdio/request-state.js +79 -0
- package/dist/stdio/search.d.ts +10 -0
- package/dist/stdio/search.js +76 -0
- package/dist/stdio/server.d.ts +29 -0
- package/dist/stdio/server.js +309 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.js +1 -0
- package/package.json +131 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { link, lstat, mkdir, open, realpath, rmdir, unlink, } from "node:fs/promises";
|
|
4
|
+
import { basename, dirname, join, relative, sep } from "node:path";
|
|
5
|
+
const compiledStates = new WeakMap();
|
|
6
|
+
/** Package-private construction gate; intentionally absent from the compiler barrel. */
|
|
7
|
+
export function registerCompiledRelease(compiled, outDir, digests, sizes, ownership) {
|
|
8
|
+
compiledStates.set(compiled, {
|
|
9
|
+
outDir,
|
|
10
|
+
paths: { ...compiled.paths },
|
|
11
|
+
consumed: false,
|
|
12
|
+
digests: { ...digests },
|
|
13
|
+
sizes: { ...sizes },
|
|
14
|
+
ownership: {
|
|
15
|
+
directory: { ...ownership.directory },
|
|
16
|
+
sqlite: { ...ownership.sqlite },
|
|
17
|
+
signature: { ...ownership.signature },
|
|
18
|
+
manifest: { ...ownership.manifest },
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
return compiled;
|
|
22
|
+
}
|
|
23
|
+
function stateFor(compiled) {
|
|
24
|
+
const state = compiledStates.get(compiled);
|
|
25
|
+
if (!state || state.consumed)
|
|
26
|
+
throw new Error("compiled release is invalid or already consumed");
|
|
27
|
+
return state;
|
|
28
|
+
}
|
|
29
|
+
export function pathIdentityFromStats(stat) {
|
|
30
|
+
if (stat.birthtimeNs <= 0n)
|
|
31
|
+
throw new Error("path identity requires a nonzero birthtimeNs");
|
|
32
|
+
return {
|
|
33
|
+
dev: stat.dev,
|
|
34
|
+
ino: stat.ino,
|
|
35
|
+
birthtimeNs: stat.birthtimeNs,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function samePathIdentity(left, right) {
|
|
39
|
+
return (left.dev === right.dev &&
|
|
40
|
+
left.ino === right.ino &&
|
|
41
|
+
left.birthtimeNs === right.birthtimeNs);
|
|
42
|
+
}
|
|
43
|
+
export async function capturePathIdentity(path, kind) {
|
|
44
|
+
const metadata = await lstat(path, { bigint: true });
|
|
45
|
+
const validKind = kind === "directory" ? metadata.isDirectory() : metadata.isFile();
|
|
46
|
+
if (!validKind || metadata.isSymbolicLink())
|
|
47
|
+
throw new Error(`compiler-created ${kind} is unsafe`);
|
|
48
|
+
if (kind === "file" && metadata.nlink !== 1n)
|
|
49
|
+
throw new Error("compiler-created file must have one link");
|
|
50
|
+
return pathIdentityFromStats(metadata);
|
|
51
|
+
}
|
|
52
|
+
async function ownedMetadata(path, identity) {
|
|
53
|
+
const metadata = await lstat(path, { bigint: true }).catch((error) => {
|
|
54
|
+
if (error.code === "ENOENT")
|
|
55
|
+
return null;
|
|
56
|
+
throw error;
|
|
57
|
+
});
|
|
58
|
+
return metadata && samePathIdentity(pathIdentityFromStats(metadata), identity)
|
|
59
|
+
? metadata
|
|
60
|
+
: null;
|
|
61
|
+
}
|
|
62
|
+
async function requireOwnedFile(path, identity, label, requireSingleLink = true) {
|
|
63
|
+
const metadata = await ownedMetadata(path, identity);
|
|
64
|
+
if (!metadata?.isFile() ||
|
|
65
|
+
metadata.isSymbolicLink() ||
|
|
66
|
+
(requireSingleLink && metadata.nlink !== 1n))
|
|
67
|
+
throw new Error(`${label} identity or ownership was lost`);
|
|
68
|
+
}
|
|
69
|
+
async function requireOwnedDirectory(path, identity, label) {
|
|
70
|
+
const metadata = await ownedMetadata(path, identity);
|
|
71
|
+
if (!metadata?.isDirectory() || metadata.isSymbolicLink())
|
|
72
|
+
throw new Error(`${label} identity or ownership was lost`);
|
|
73
|
+
}
|
|
74
|
+
async function removeOwnedFile(path, identity) {
|
|
75
|
+
const metadata = await ownedMetadata(path, identity);
|
|
76
|
+
if (!metadata?.isFile() || metadata.isSymbolicLink())
|
|
77
|
+
return false;
|
|
78
|
+
await unlink(path);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
export async function cleanupOwnedStage(paths, ownership) {
|
|
82
|
+
for (const kind of ["manifest", "signature", "sqlite"]) {
|
|
83
|
+
const identity = ownership[kind];
|
|
84
|
+
if (identity)
|
|
85
|
+
await removeOwnedFile(paths[kind], identity).catch(() => false);
|
|
86
|
+
}
|
|
87
|
+
if (!ownership.directory)
|
|
88
|
+
return;
|
|
89
|
+
const directory = await ownedMetadata(paths.directory, ownership.directory);
|
|
90
|
+
if (!directory?.isDirectory() || directory.isSymbolicLink())
|
|
91
|
+
return;
|
|
92
|
+
await rmdir(paths.directory).catch(() => { });
|
|
93
|
+
}
|
|
94
|
+
async function hashOwnedStageFile(path, identity, expectedSize, beforeOpen, beforeRead) {
|
|
95
|
+
if (!Number.isSafeInteger(expectedSize) || expectedSize < 0)
|
|
96
|
+
throw new Error("compiled release staged file size is invalid");
|
|
97
|
+
if (typeof constants.O_NOFOLLOW !== "number" || constants.O_NOFOLLOW === 0)
|
|
98
|
+
throw new Error("compiled release validation requires O_NOFOLLOW support");
|
|
99
|
+
await requireOwnedFile(path, identity, "compiled release staged file");
|
|
100
|
+
await beforeOpen();
|
|
101
|
+
let handle;
|
|
102
|
+
try {
|
|
103
|
+
handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
throw new Error("compiled release staged file could not be opened safely");
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const before = await handle.stat({ bigint: true });
|
|
110
|
+
if (!before.isFile() ||
|
|
111
|
+
before.nlink !== 1n ||
|
|
112
|
+
!samePathIdentity(pathIdentityFromStats(before), identity) ||
|
|
113
|
+
before.size !== BigInt(expectedSize))
|
|
114
|
+
throw new Error("compiled release staged content was modified or identity was lost");
|
|
115
|
+
await requireOwnedFile(path, identity, "compiled release staged file");
|
|
116
|
+
await beforeRead();
|
|
117
|
+
const digest = createHash("sha256");
|
|
118
|
+
const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, expectedSize) || 1);
|
|
119
|
+
let total = 0;
|
|
120
|
+
while (total < expectedSize) {
|
|
121
|
+
const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.byteLength, expectedSize - total), null);
|
|
122
|
+
if (bytesRead === 0)
|
|
123
|
+
throw new Error("compiled release staged content was modified: early EOF");
|
|
124
|
+
digest.update(buffer.subarray(0, bytesRead));
|
|
125
|
+
total += bytesRead;
|
|
126
|
+
}
|
|
127
|
+
const growthProbe = new Uint8Array(1);
|
|
128
|
+
const { bytesRead: extraBytes } = await handle.read(growthProbe, 0, 1, null);
|
|
129
|
+
if (extraBytes !== 0)
|
|
130
|
+
throw new Error("compiled release staged content was modified: exceeds recorded size");
|
|
131
|
+
const after = await handle.stat({ bigint: true });
|
|
132
|
+
if (!after.isFile() ||
|
|
133
|
+
after.nlink !== 1n ||
|
|
134
|
+
!samePathIdentity(pathIdentityFromStats(after), identity) ||
|
|
135
|
+
after.size !== BigInt(expectedSize))
|
|
136
|
+
throw new Error("compiled release staged content was modified or identity was lost");
|
|
137
|
+
await requireOwnedFile(path, identity, "compiled release staged file");
|
|
138
|
+
return digest.digest("hex");
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
await handle.close();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function validateStage(compiled, state, checkpoint) {
|
|
145
|
+
const root = await realpath(state.outDir);
|
|
146
|
+
if (compiled.paths.directory !== state.paths.directory)
|
|
147
|
+
throw new Error("compiled release staged directory was substituted");
|
|
148
|
+
await requireOwnedDirectory(state.paths.directory, state.ownership.directory, "compiled release stage");
|
|
149
|
+
const directory = await realpath(state.paths.directory);
|
|
150
|
+
if (dirname(directory) !== root || relative(root, directory).includes(sep))
|
|
151
|
+
throw new Error("compiled release stage is outside its output directory");
|
|
152
|
+
for (const kind of ["sqlite", "signature", "manifest"]) {
|
|
153
|
+
const path = state.paths[kind];
|
|
154
|
+
if (compiled.paths[kind] !== path || dirname(path) !== directory)
|
|
155
|
+
throw new Error("compiled release staged path was substituted");
|
|
156
|
+
await requireOwnedFile(path, state.ownership[kind], "compiled release staged file");
|
|
157
|
+
const digest = await hashOwnedStageFile(path, state.ownership[kind], state.sizes[kind], () => checkpoint(`before-${kind}-validation-open`), () => checkpoint(`before-${kind}-validation-read`));
|
|
158
|
+
await requireOwnedFile(path, state.ownership[kind], "compiled release staged file");
|
|
159
|
+
if (digest !== state.digests[kind])
|
|
160
|
+
throw new Error("compiled release staged content was modified");
|
|
161
|
+
}
|
|
162
|
+
if (basename(state.paths.sqlite) !== `${compiled.manifest.releaseId}.sqlite` ||
|
|
163
|
+
basename(state.paths.signature) !==
|
|
164
|
+
`${compiled.manifest.releaseId}.manifest.sig` ||
|
|
165
|
+
basename(state.paths.manifest) !==
|
|
166
|
+
`${compiled.manifest.releaseId}.manifest.json`)
|
|
167
|
+
throw new Error("compiled release staged names disagree with its release ID");
|
|
168
|
+
}
|
|
169
|
+
async function syncFile(path, identity) {
|
|
170
|
+
const handle = await open(path, "r");
|
|
171
|
+
try {
|
|
172
|
+
const metadata = await handle.stat({ bigint: true });
|
|
173
|
+
if (!metadata.isFile() ||
|
|
174
|
+
!samePathIdentity(pathIdentityFromStats(metadata), identity))
|
|
175
|
+
throw new Error("compiled release staged file identity was lost");
|
|
176
|
+
await handle.sync();
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
await handle.close();
|
|
180
|
+
}
|
|
181
|
+
await requireOwnedFile(path, identity, "compiled release staged file");
|
|
182
|
+
}
|
|
183
|
+
async function syncOwnedDirectory(path, identity) {
|
|
184
|
+
await requireOwnedDirectory(path, identity, "release target directory");
|
|
185
|
+
const handle = await open(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
186
|
+
try {
|
|
187
|
+
const metadata = await handle.stat({ bigint: true });
|
|
188
|
+
if (!metadata.isDirectory() ||
|
|
189
|
+
!samePathIdentity(pathIdentityFromStats(metadata), identity))
|
|
190
|
+
throw new Error("release target directory identity or ownership was lost");
|
|
191
|
+
await handle.sync();
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
await handle.close();
|
|
195
|
+
}
|
|
196
|
+
await requireOwnedDirectory(path, identity, "release target directory");
|
|
197
|
+
}
|
|
198
|
+
async function requirePromotedTargets(targets) {
|
|
199
|
+
for (const [path, identity] of targets)
|
|
200
|
+
await requireOwnedFile(path, identity, "published target");
|
|
201
|
+
}
|
|
202
|
+
async function exists(path) {
|
|
203
|
+
try {
|
|
204
|
+
await lstat(path);
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
if (error.code === "ENOENT")
|
|
209
|
+
return false;
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function requireLock(path, identity) {
|
|
214
|
+
const metadata = await ownedMetadata(path, identity);
|
|
215
|
+
if (!metadata?.isFile() ||
|
|
216
|
+
metadata.isSymbolicLink() ||
|
|
217
|
+
metadata.nlink !== 1n ||
|
|
218
|
+
(metadata.mode & 511n) !== 384n)
|
|
219
|
+
throw new Error("release publication lock identity or ownership was lost");
|
|
220
|
+
}
|
|
221
|
+
export async function publishReleaseWithCheckpoint(compiled, target, checkpoint = () => { }) {
|
|
222
|
+
const state = stateFor(compiled);
|
|
223
|
+
state.consumed = true;
|
|
224
|
+
const createdTargets = new Map();
|
|
225
|
+
let lockHandle;
|
|
226
|
+
let lockIdentity;
|
|
227
|
+
let lockPath;
|
|
228
|
+
let targetDirectory;
|
|
229
|
+
let targetDirectoryIdentity;
|
|
230
|
+
try {
|
|
231
|
+
await validateStage(compiled, state, checkpoint);
|
|
232
|
+
await mkdir(target.directory, { recursive: false }).catch((error) => {
|
|
233
|
+
if (error.code !== "EEXIST")
|
|
234
|
+
throw error;
|
|
235
|
+
});
|
|
236
|
+
targetDirectory = await realpath(target.directory);
|
|
237
|
+
targetDirectoryIdentity = await capturePathIdentity(targetDirectory, "directory");
|
|
238
|
+
await requireOwnedDirectory(targetDirectory, targetDirectoryIdentity, "release target directory");
|
|
239
|
+
lockPath = join(targetDirectory, `.${compiled.manifest.releaseId}.publish.lock`);
|
|
240
|
+
if (typeof constants.O_NOFOLLOW !== "number" || constants.O_NOFOLLOW === 0)
|
|
241
|
+
throw new Error("release publication requires O_NOFOLLOW support");
|
|
242
|
+
const flags = constants.O_CREAT |
|
|
243
|
+
constants.O_EXCL |
|
|
244
|
+
constants.O_WRONLY |
|
|
245
|
+
constants.O_NOFOLLOW;
|
|
246
|
+
lockHandle = await open(lockPath, flags, 0o600).catch((error) => {
|
|
247
|
+
if (error.code === "EEXIST")
|
|
248
|
+
throw new Error("release publication lock already exists; publication is in progress or requires operator cleanup");
|
|
249
|
+
throw error;
|
|
250
|
+
});
|
|
251
|
+
await requireOwnedDirectory(targetDirectory, targetDirectoryIdentity, "release target directory");
|
|
252
|
+
const lockStat = await lockHandle.stat({ bigint: true });
|
|
253
|
+
if (!lockStat.isFile() ||
|
|
254
|
+
lockStat.nlink !== 1n ||
|
|
255
|
+
(lockStat.mode & 511n) !== 384n)
|
|
256
|
+
throw new Error("release publication lock is unsafe");
|
|
257
|
+
lockIdentity = pathIdentityFromStats(lockStat);
|
|
258
|
+
await requireLock(lockPath, lockIdentity);
|
|
259
|
+
const targets = [
|
|
260
|
+
join(targetDirectory, `${compiled.manifest.releaseId}.sqlite`),
|
|
261
|
+
join(targetDirectory, `${compiled.manifest.releaseId}.manifest.sig`),
|
|
262
|
+
join(targetDirectory, `${compiled.manifest.releaseId}.manifest.json`),
|
|
263
|
+
];
|
|
264
|
+
if ((await Promise.all(targets.map(exists))).some(Boolean))
|
|
265
|
+
throw new Error("release target already exists");
|
|
266
|
+
const sources = [
|
|
267
|
+
state.paths.sqlite,
|
|
268
|
+
state.paths.signature,
|
|
269
|
+
state.paths.manifest,
|
|
270
|
+
];
|
|
271
|
+
const identities = [
|
|
272
|
+
state.ownership.sqlite,
|
|
273
|
+
state.ownership.signature,
|
|
274
|
+
state.ownership.manifest,
|
|
275
|
+
];
|
|
276
|
+
const checkpoints = [
|
|
277
|
+
"payload-published",
|
|
278
|
+
"signature-published",
|
|
279
|
+
"manifest-published",
|
|
280
|
+
];
|
|
281
|
+
for (let index = 0; index < sources.length; index += 1) {
|
|
282
|
+
const source = sources[index];
|
|
283
|
+
const destination = targets[index];
|
|
284
|
+
const identity = identities[index];
|
|
285
|
+
await requireLock(lockPath, lockIdentity);
|
|
286
|
+
await requireOwnedDirectory(targetDirectory, targetDirectoryIdentity, "release target directory");
|
|
287
|
+
await syncFile(source, identity);
|
|
288
|
+
await requirePromotedTargets(createdTargets);
|
|
289
|
+
try {
|
|
290
|
+
await link(source, destination);
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
if (error.code === "EEXIST")
|
|
294
|
+
throw new Error("release target already exists", { cause: error });
|
|
295
|
+
throw error;
|
|
296
|
+
}
|
|
297
|
+
const destinationMetadata = await lstat(destination, { bigint: true });
|
|
298
|
+
if (!destinationMetadata.isFile() ||
|
|
299
|
+
destinationMetadata.isSymbolicLink() ||
|
|
300
|
+
!samePathIdentity(pathIdentityFromStats(destinationMetadata), identity))
|
|
301
|
+
throw new Error("published target identity is unsafe");
|
|
302
|
+
createdTargets.set(destination, identity);
|
|
303
|
+
await requireOwnedDirectory(targetDirectory, targetDirectoryIdentity, "release target directory");
|
|
304
|
+
await requireLock(lockPath, lockIdentity);
|
|
305
|
+
if (!(await removeOwnedFile(source, identity)))
|
|
306
|
+
throw new Error("compiled release staged file identity was lost");
|
|
307
|
+
await syncOwnedDirectory(targetDirectory, targetDirectoryIdentity);
|
|
308
|
+
await checkpoint(checkpoints[index]);
|
|
309
|
+
await requireOwnedDirectory(targetDirectory, targetDirectoryIdentity, "release target directory");
|
|
310
|
+
await requirePromotedTargets(createdTargets);
|
|
311
|
+
await requireLock(lockPath, lockIdentity);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
const targetDirectoryOwned = targetDirectory && targetDirectoryIdentity
|
|
316
|
+
? await ownedMetadata(targetDirectory, targetDirectoryIdentity).then((metadata) => metadata?.isDirectory() === true, () => false)
|
|
317
|
+
: false;
|
|
318
|
+
if (targetDirectoryOwned) {
|
|
319
|
+
for (const [path, identity] of [...createdTargets.entries()].reverse())
|
|
320
|
+
await removeOwnedFile(path, identity).catch(() => false);
|
|
321
|
+
if (createdTargets.size > 0)
|
|
322
|
+
await syncOwnedDirectory(targetDirectory, targetDirectoryIdentity).catch(() => { });
|
|
323
|
+
}
|
|
324
|
+
throw error;
|
|
325
|
+
}
|
|
326
|
+
finally {
|
|
327
|
+
await lockHandle?.close().catch(() => { });
|
|
328
|
+
if (lockPath &&
|
|
329
|
+
lockIdentity &&
|
|
330
|
+
targetDirectory &&
|
|
331
|
+
targetDirectoryIdentity &&
|
|
332
|
+
(await ownedMetadata(targetDirectory, targetDirectoryIdentity).then((metadata) => metadata?.isDirectory() === true, () => false))) {
|
|
333
|
+
await removeOwnedFile(lockPath, lockIdentity).catch(() => false);
|
|
334
|
+
await syncOwnedDirectory(targetDirectory, targetDirectoryIdentity).catch(() => { });
|
|
335
|
+
}
|
|
336
|
+
await cleanupOwnedStage(state.paths, state.ownership);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
export async function publishRelease(compiled, target) {
|
|
340
|
+
await publishReleaseWithCheckpoint(compiled, target);
|
|
341
|
+
}
|
|
342
|
+
export async function discardCompiledRelease(compiled) {
|
|
343
|
+
const state = stateFor(compiled);
|
|
344
|
+
state.consumed = true;
|
|
345
|
+
await cleanupOwnedStage(state.paths, state.ownership);
|
|
346
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Sha256 } from "../runtime/types.ts";
|
|
2
|
+
import { type CompilerLimits } from "./load-v4.ts";
|
|
3
|
+
export interface ReferenceMapEntryV1 {
|
|
4
|
+
readonly uri: string;
|
|
5
|
+
readonly file: string;
|
|
6
|
+
readonly sha256: Sha256;
|
|
7
|
+
}
|
|
8
|
+
export interface ReferenceMapV1 {
|
|
9
|
+
readonly version: 1;
|
|
10
|
+
readonly entries: readonly ReferenceMapEntryV1[];
|
|
11
|
+
}
|
|
12
|
+
export interface ResolvedReferenceV1 {
|
|
13
|
+
readonly bytes: Uint8Array;
|
|
14
|
+
readonly mediaType: "json" | "yaml";
|
|
15
|
+
}
|
|
16
|
+
export interface ReferenceResolver {
|
|
17
|
+
resolve(uri: string): Promise<ResolvedReferenceV1>;
|
|
18
|
+
}
|
|
19
|
+
export interface ResolvedDocumentV4 {
|
|
20
|
+
readonly uri: string;
|
|
21
|
+
readonly document: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
export declare function normalizeHttpsUri(value: string): string;
|
|
24
|
+
export declare function sourceUriFromLabel(label: string): string;
|
|
25
|
+
export declare function normalizeGraphUri(value: string, baseUri: string): string;
|
|
26
|
+
export declare function loadReferenceMap(mapPath: string, sourceUri: string, limits?: CompilerLimits): Promise<ReferenceMapV1>;
|
|
27
|
+
export declare function referenceGraphDigest(map: ReferenceMapV1): Promise<Sha256>;
|
|
28
|
+
interface ReferenceReadTestCheckpoint {
|
|
29
|
+
beforeOpen(): void | Promise<void>;
|
|
30
|
+
onRead(): void;
|
|
31
|
+
}
|
|
32
|
+
export declare class ReferenceGraphV4 {
|
|
33
|
+
#private;
|
|
34
|
+
readonly sourceUri: string;
|
|
35
|
+
readonly map: ReferenceMapV1;
|
|
36
|
+
/** @internal Test-only hook; this module is not exported by the package. */
|
|
37
|
+
static setReferenceReadCheckpointForTesting(checkpoint: ReferenceReadTestCheckpoint | undefined): void;
|
|
38
|
+
private constructor();
|
|
39
|
+
static create(options: {
|
|
40
|
+
sourceUri: string;
|
|
41
|
+
mapPath?: string;
|
|
42
|
+
referenceRoot?: string;
|
|
43
|
+
resolver?: ReferenceResolver;
|
|
44
|
+
limits: CompilerLimits;
|
|
45
|
+
rootBytes: number;
|
|
46
|
+
}): Promise<ReferenceGraphV4>;
|
|
47
|
+
resolve(rawRef: string, fromUri: string, depth: number): Promise<{
|
|
48
|
+
uri: string;
|
|
49
|
+
pointer: string;
|
|
50
|
+
document: Record<string, unknown>;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
export {};
|