@zuplo/cli 7.9.12 → 7.9.13
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/dist/__tests__/integration/source-upgrade.integration.test.d.ts +2 -0
- package/dist/__tests__/integration/source-upgrade.integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration/source-upgrade.integration.test.js +100 -0
- package/dist/__tests__/integration/source-upgrade.integration.test.js.map +1 -0
- package/dist/cmds/source/upgrade.d.ts.map +1 -1
- package/dist/cmds/source/upgrade.js +45 -2
- package/dist/cmds/source/upgrade.js.map +1 -1
- package/dist/source/ai-gateway/catalog.json +411 -0
- package/dist/source/ai-gateway/files.d.ts +16 -0
- package/dist/source/ai-gateway/files.d.ts.map +1 -0
- package/dist/source/ai-gateway/files.js +174 -0
- package/dist/source/ai-gateway/files.js.map +1 -0
- package/dist/source/ai-gateway/files.test.d.ts +2 -0
- package/dist/source/ai-gateway/files.test.d.ts.map +1 -0
- package/dist/source/ai-gateway/files.test.js +135 -0
- package/dist/source/ai-gateway/files.test.js.map +1 -0
- package/dist/source/ai-gateway/handler.d.ts +31 -0
- package/dist/source/ai-gateway/handler.d.ts.map +1 -0
- package/dist/source/ai-gateway/handler.js +140 -0
- package/dist/source/ai-gateway/handler.js.map +1 -0
- package/dist/source/ai-gateway/handler.test.d.ts +2 -0
- package/dist/source/ai-gateway/handler.test.d.ts.map +1 -0
- package/dist/source/ai-gateway/handler.test.js +113 -0
- package/dist/source/ai-gateway/handler.test.js.map +1 -0
- package/dist/source/ai-gateway/package.test.d.ts +2 -0
- package/dist/source/ai-gateway/package.test.d.ts.map +1 -0
- package/dist/source/ai-gateway/package.test.js +30 -0
- package/dist/source/ai-gateway/package.test.js.map +1 -0
- package/dist/source/ai-gateway/planner.d.ts +132 -0
- package/dist/source/ai-gateway/planner.d.ts.map +1 -0
- package/dist/source/ai-gateway/planner.js +365 -0
- package/dist/source/ai-gateway/planner.js.map +1 -0
- package/dist/source/ai-gateway/planner.test.d.ts +2 -0
- package/dist/source/ai-gateway/planner.test.d.ts.map +1 -0
- package/dist/source/ai-gateway/planner.test.js +282 -0
- package/dist/source/ai-gateway/planner.test.js.map +1 -0
- package/dist/source/ai-gateway/release.d.ts +19 -0
- package/dist/source/ai-gateway/release.d.ts.map +1 -0
- package/dist/source/ai-gateway/release.js +75 -0
- package/dist/source/ai-gateway/release.js.map +1 -0
- package/dist/source/ai-gateway/release.test.d.ts +2 -0
- package/dist/source/ai-gateway/release.test.d.ts.map +1 -0
- package/dist/source/ai-gateway/release.test.js +62 -0
- package/dist/source/ai-gateway/release.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@zuplo/core/package.json +1 -1
- package/node_modules/@zuplo/graphql/package.json +1 -1
- package/node_modules/@zuplo/openapi-tools/package.json +1 -1
- package/node_modules/@zuplo/otel/package.json +1 -1
- package/node_modules/@zuplo/runtime/package.json +1 -1
- package/package.json +11 -11
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { isDeepStrictEqual } from "node:util";
|
|
3
|
+
import { applyEdits, modify, parseTree } from "jsonc-parser";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import bundledCatalog from "./catalog.json" with { type: "json" };
|
|
6
|
+
const handlerSchema = z.looseObject({
|
|
7
|
+
module: z.string(),
|
|
8
|
+
export: z.string(),
|
|
9
|
+
options: z.record(z.string(), z.unknown()).optional(),
|
|
10
|
+
});
|
|
11
|
+
const policySchema = z.looseObject({
|
|
12
|
+
name: z.string(),
|
|
13
|
+
policyType: z.string().optional(),
|
|
14
|
+
handler: handlerSchema,
|
|
15
|
+
});
|
|
16
|
+
const policiesSchema = z.looseObject({ policies: z.array(policySchema) });
|
|
17
|
+
const oasSchema = z.looseObject({
|
|
18
|
+
openapi: z.string(),
|
|
19
|
+
paths: z.record(z.string(), z.record(z.string(), z.unknown())),
|
|
20
|
+
});
|
|
21
|
+
const operationSchema = z.looseObject({
|
|
22
|
+
operationId: z.string(),
|
|
23
|
+
"x-zuplo-route": z.looseObject({
|
|
24
|
+
handler: handlerSchema,
|
|
25
|
+
policies: z.looseObject({ inbound: z.array(z.string()) }),
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
const stepSchema = z.object({
|
|
29
|
+
id: z.string(),
|
|
30
|
+
kind: z.enum(["route", "policy"]),
|
|
31
|
+
key: z.string(),
|
|
32
|
+
target: z.string(),
|
|
33
|
+
runtime: z.string(),
|
|
34
|
+
instructions: z.string(),
|
|
35
|
+
});
|
|
36
|
+
export const catalogSchema = z.object({
|
|
37
|
+
formatVersion: z.literal(1),
|
|
38
|
+
target: z.string(),
|
|
39
|
+
aliases: z.record(z.string(), z.string()),
|
|
40
|
+
snapshots: z.array(z.object({
|
|
41
|
+
revision: z.string(),
|
|
42
|
+
sourceCommit: z.string(),
|
|
43
|
+
files: z.record(z.string(), z.string()),
|
|
44
|
+
})),
|
|
45
|
+
steps: z.array(stepSchema),
|
|
46
|
+
assessments: z.array(z.object({
|
|
47
|
+
revision: z.string(),
|
|
48
|
+
classification: z.enum(["additions", "manual", "documentation-only"]),
|
|
49
|
+
instructions: z.string(),
|
|
50
|
+
})),
|
|
51
|
+
});
|
|
52
|
+
export const catalog = catalogSchema.parse(bundledCatalog);
|
|
53
|
+
export const provenanceSchema = z
|
|
54
|
+
.object({
|
|
55
|
+
formatVersion: z.literal(1),
|
|
56
|
+
template: z.literal("ai-gateway"),
|
|
57
|
+
origin: z.object({ revision: z.string(), release: z.string() }).nullable(),
|
|
58
|
+
appliedSteps: z.array(z.string()),
|
|
59
|
+
})
|
|
60
|
+
.strict();
|
|
61
|
+
export const AI_OAS = "config/ai.oas.json";
|
|
62
|
+
export const POLICIES = "config/policies.json";
|
|
63
|
+
export const PROVENANCE = ".zuplo-template.json";
|
|
64
|
+
const CATCH_ALL = "/:app_id/(.*)";
|
|
65
|
+
function isMethodKey(key) {
|
|
66
|
+
return key
|
|
67
|
+
.split(",")
|
|
68
|
+
.every((method) => /^(get|post|put|patch|delete|head|options|trace)$/i.test(method.trim()));
|
|
69
|
+
}
|
|
70
|
+
export function readJson(text, file) {
|
|
71
|
+
const errors = [];
|
|
72
|
+
const tree = parseTree(text, errors, {
|
|
73
|
+
allowTrailingComma: file.endsWith(".jsonc"),
|
|
74
|
+
disallowComments: !file.endsWith(".jsonc"),
|
|
75
|
+
});
|
|
76
|
+
if (!tree || errors.length)
|
|
77
|
+
throw new Error(`${file}: malformed JSON; repair it before upgrading.`);
|
|
78
|
+
function visit(node) {
|
|
79
|
+
if (node.type === "object") {
|
|
80
|
+
const keys = new Set();
|
|
81
|
+
for (const property of node.children ?? []) {
|
|
82
|
+
const key = property.children?.[0]?.value;
|
|
83
|
+
if (typeof key !== "string" || keys.has(key)) {
|
|
84
|
+
throw new Error(`${file}: duplicate or invalid key ${String(key)}; resolve it manually.`);
|
|
85
|
+
}
|
|
86
|
+
keys.add(key);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const child of node.children ?? [])
|
|
90
|
+
visit(child);
|
|
91
|
+
}
|
|
92
|
+
visit(tree);
|
|
93
|
+
function value(node) {
|
|
94
|
+
if (node.type === "object")
|
|
95
|
+
return Object.fromEntries((node.children ?? []).map((p) => [
|
|
96
|
+
p.children[0].value,
|
|
97
|
+
value(p.children[1]),
|
|
98
|
+
]));
|
|
99
|
+
if (node.type === "array")
|
|
100
|
+
return (node.children ?? []).map(value);
|
|
101
|
+
return node.value;
|
|
102
|
+
}
|
|
103
|
+
return value(tree);
|
|
104
|
+
}
|
|
105
|
+
export function contentHash(text) {
|
|
106
|
+
return createHash("sha256").update(text).digest("hex");
|
|
107
|
+
}
|
|
108
|
+
function edit(text, pointer, value, index) {
|
|
109
|
+
const indent = text.match(/\n([\t ]+)"/)?.[1] ?? " ";
|
|
110
|
+
return applyEdits(text, modify(text, pointer, value, {
|
|
111
|
+
formattingOptions: {
|
|
112
|
+
insertSpaces: !indent.includes("\t"),
|
|
113
|
+
tabSize: indent.length,
|
|
114
|
+
eol: text.includes("\r\n") ? "\r\n" : "\n",
|
|
115
|
+
},
|
|
116
|
+
...(index === undefined ? {} : { getInsertionIndex: () => index }),
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
export function planUpgrade(files, release, data = catalog) {
|
|
120
|
+
const plan = {
|
|
121
|
+
target: data.target,
|
|
122
|
+
release,
|
|
123
|
+
blockers: [],
|
|
124
|
+
notes: [],
|
|
125
|
+
additions: [],
|
|
126
|
+
before: { ...files },
|
|
127
|
+
after: {},
|
|
128
|
+
};
|
|
129
|
+
const block = (message) => plan.blockers.push(message);
|
|
130
|
+
try {
|
|
131
|
+
const target = data.snapshots.find((s) => s.revision === data.target);
|
|
132
|
+
if (!target)
|
|
133
|
+
throw new Error("The bundled target snapshot is missing. Install a complete CLI release.");
|
|
134
|
+
const targetOas = oasSchema.parse(readJson(target.files[AI_OAS], AI_OAS));
|
|
135
|
+
const targetPolicies = policiesSchema.parse(readJson(target.files[POLICIES], POLICIES));
|
|
136
|
+
for (const step of data.steps.filter((s) => s.target === data.target)) {
|
|
137
|
+
const entry = step.kind === "route"
|
|
138
|
+
? targetOas.paths[step.key]
|
|
139
|
+
: targetPolicies.policies.find((p) => p.name === step.key);
|
|
140
|
+
if (!entry)
|
|
141
|
+
throw new Error(`Bundled upgrade ${step.id} has no definition.`);
|
|
142
|
+
plan.additions.push({
|
|
143
|
+
step,
|
|
144
|
+
file: step.kind === "route" ? AI_OAS : POLICIES,
|
|
145
|
+
key: step.key,
|
|
146
|
+
value: entry,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const oas = oasSchema.parse(readJson(files[AI_OAS], AI_OAS));
|
|
150
|
+
const policies = policiesSchema.parse(readJson(files[POLICIES], POLICIES));
|
|
151
|
+
const canonical = (name) => data.aliases[name] ?? name;
|
|
152
|
+
function comparableRoute(value) {
|
|
153
|
+
if (value === undefined)
|
|
154
|
+
return value;
|
|
155
|
+
const route = z
|
|
156
|
+
.record(z.string(), z.unknown())
|
|
157
|
+
.parse(structuredClone(value));
|
|
158
|
+
for (const [method, raw] of Object.entries(route)) {
|
|
159
|
+
if (!isMethodKey(method))
|
|
160
|
+
continue;
|
|
161
|
+
const result = operationSchema.safeParse(raw);
|
|
162
|
+
if (result.success) {
|
|
163
|
+
result.data["x-zuplo-route"].policies.inbound =
|
|
164
|
+
result.data["x-zuplo-route"].policies.inbound.map(canonical);
|
|
165
|
+
route[method] = result.data;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return route;
|
|
169
|
+
}
|
|
170
|
+
const sameRoute = (a, b) => isDeepStrictEqual(comparableRoute(a), comparableRoute(b));
|
|
171
|
+
const byName = new Map();
|
|
172
|
+
for (const policy of policies.policies) {
|
|
173
|
+
const name = canonical(policy.name);
|
|
174
|
+
if (byName.has(name))
|
|
175
|
+
block(`${POLICIES}: duplicate policy identity "${name}" (including historical aliases).`);
|
|
176
|
+
byName.set(name, policy);
|
|
177
|
+
}
|
|
178
|
+
const profiles = data.snapshots.map((s) => ({
|
|
179
|
+
snapshot: s,
|
|
180
|
+
oas: oasSchema.parse(readJson(s.files[AI_OAS], AI_OAS)),
|
|
181
|
+
policies: policiesSchema.parse(readJson(s.files[POLICIES], POLICIES))
|
|
182
|
+
.policies,
|
|
183
|
+
}));
|
|
184
|
+
let provenance = {
|
|
185
|
+
formatVersion: 1,
|
|
186
|
+
template: "ai-gateway",
|
|
187
|
+
origin: null,
|
|
188
|
+
appliedSteps: [],
|
|
189
|
+
};
|
|
190
|
+
if (files[PROVENANCE] !== undefined)
|
|
191
|
+
provenance = provenanceSchema.parse(readJson(files[PROVENANCE], PROVENANCE));
|
|
192
|
+
if (provenance.origin &&
|
|
193
|
+
!data.snapshots.some((s) => s.revision === provenance.origin?.revision))
|
|
194
|
+
block(`${PROVENANCE}: originating snapshot is unavailable; use manual instructions.`);
|
|
195
|
+
if (provenance.appliedSteps.some((id) => !data.steps.some((s) => s.id === id)))
|
|
196
|
+
block(`${PROVENANCE}: unknown upgrade step; use a CLI that supports this project.`);
|
|
197
|
+
const original = profiles.find((p) => p.snapshot.revision === provenance.origin?.revision);
|
|
198
|
+
const alreadyPresent = new Set();
|
|
199
|
+
plan.additions = plan.additions.filter((addition) => {
|
|
200
|
+
const present = addition.step.kind === "route"
|
|
201
|
+
? oas.paths[addition.key] !== undefined
|
|
202
|
+
: byName.has(canonical(addition.key));
|
|
203
|
+
if (present)
|
|
204
|
+
alreadyPresent.add(addition.step.id);
|
|
205
|
+
if (!present &&
|
|
206
|
+
(provenance.appliedSteps.some((id) => data.steps.some((s) => s.id === id &&
|
|
207
|
+
s.kind === addition.step.kind &&
|
|
208
|
+
(s.kind === "policy"
|
|
209
|
+
? canonical(s.key) === canonical(addition.step.key)
|
|
210
|
+
: s.key === addition.step.key))) ||
|
|
211
|
+
(original &&
|
|
212
|
+
(addition.step.kind === "route"
|
|
213
|
+
? original.oas.paths[addition.key] !== undefined
|
|
214
|
+
: original.policies.some((p) => canonical(p.name) === canonical(addition.key)))))) {
|
|
215
|
+
block(`${addition.file}: "${addition.key}" was previously present and is now missing; restore it manually if its removal was unintended.`);
|
|
216
|
+
}
|
|
217
|
+
return !present;
|
|
218
|
+
});
|
|
219
|
+
const originIndex = data.snapshots.findIndex((s) => s.revision === provenance.origin?.revision);
|
|
220
|
+
const targetIndex = data.snapshots.findIndex((s) => s.revision === data.target);
|
|
221
|
+
for (const assessment of data.assessments) {
|
|
222
|
+
const index = data.snapshots.findIndex((s) => s.revision === assessment.revision);
|
|
223
|
+
if (index <= originIndex || index > targetIndex)
|
|
224
|
+
continue;
|
|
225
|
+
if (assessment.revision === data.target ||
|
|
226
|
+
assessment.classification === "manual")
|
|
227
|
+
plan.notes.push(assessment.instructions);
|
|
228
|
+
if (assessment.classification === "manual")
|
|
229
|
+
block(assessment.instructions);
|
|
230
|
+
}
|
|
231
|
+
if (!plan.additions.length)
|
|
232
|
+
return plan;
|
|
233
|
+
const root = ({ paths: _paths, info: _info, ...rest }) => rest;
|
|
234
|
+
if (!profiles.some((p) => isDeepStrictEqual(root(oas), root(p.oas))))
|
|
235
|
+
block(`${AI_OAS}: customized document-level routing/security settings require manual review.`);
|
|
236
|
+
const options = readJson(files["zuplo.jsonc"], "zuplo.jsonc");
|
|
237
|
+
if (!profiles.some((p) => isDeepStrictEqual(options, readJson(p.snapshot.files["zuplo.jsonc"], "zuplo.jsonc"))))
|
|
238
|
+
block("zuplo.jsonc: unrecognized configuration; verify routing and runtime extensions manually.");
|
|
239
|
+
for (const [file, text] of Object.entries(files).filter(([f]) => /(^|\/)zuplo\.runtime\.[^/]+$/.test(f))) {
|
|
240
|
+
if (!data.snapshots.some((s) => s.files[file]?.replaceAll("\r\n", "\n") ===
|
|
241
|
+
text.replaceAll("\r\n", "\n")))
|
|
242
|
+
block(`${file}: custom runtime extensions may affect routing; manual review required.`);
|
|
243
|
+
}
|
|
244
|
+
if (!oas.paths[CATCH_ALL] ||
|
|
245
|
+
!profiles.some((p) => sameRoute(p.oas.paths[CATCH_ALL], oas.paths[CATCH_ALL])))
|
|
246
|
+
block(`${AI_OAS}: the existing ${CATCH_ALL} route is not a supported template definition. New routes could bypass custom authentication.`);
|
|
247
|
+
for (const [key, route] of Object.entries(oas.paths)) {
|
|
248
|
+
if (targetOas.paths[key] &&
|
|
249
|
+
!profiles.some((p) => sameRoute(p.oas.paths[key], route)))
|
|
250
|
+
block(`${AI_OAS}: customized route "${key}" requires manual review.`);
|
|
251
|
+
}
|
|
252
|
+
const existingOrder = Object.keys(oas.paths).filter((key) => targetOas.paths[key]);
|
|
253
|
+
const expectedOrder = Object.keys(targetOas.paths).filter((key) => oas.paths[key]);
|
|
254
|
+
if (!isDeepStrictEqual(existingOrder, expectedOrder))
|
|
255
|
+
block(`${AI_OAS}: existing gateway route precedence differs from the template; restore the intended order manually.`);
|
|
256
|
+
const routeAdditions = plan.additions.filter((a) => a.step.kind === "route");
|
|
257
|
+
const operationIds = new Set();
|
|
258
|
+
const addedPrefixes = routeAdditions.map((a) => a.key.split("/")[1]);
|
|
259
|
+
for (const [file, text] of Object.entries(files).filter(([f]) => /^config\/.*\.oas\.json$/.test(f))) {
|
|
260
|
+
const document = oasSchema.parse(readJson(text, file));
|
|
261
|
+
for (const [key, pathItem] of Object.entries(document.paths)) {
|
|
262
|
+
for (const [methods, operation] of Object.entries(pathItem)) {
|
|
263
|
+
if (!isMethodKey(methods))
|
|
264
|
+
continue;
|
|
265
|
+
const parsed = z
|
|
266
|
+
.looseObject({ operationId: z.string().optional() })
|
|
267
|
+
.parse(operation);
|
|
268
|
+
if (parsed.operationId) {
|
|
269
|
+
if (operationIds.has(parsed.operationId))
|
|
270
|
+
block(`${file}: duplicate operationId "${parsed.operationId}".`);
|
|
271
|
+
operationIds.add(parsed.operationId);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (!routeAdditions.length || (file === AI_OAS && targetOas.paths[key]))
|
|
275
|
+
continue;
|
|
276
|
+
const prefix = key.match(/^\/([A-Za-z0-9_-]+)(?:\/|$)/)?.[1];
|
|
277
|
+
if (!prefix ||
|
|
278
|
+
addedPrefixes.includes(prefix) ||
|
|
279
|
+
JSON.stringify(pathItem).includes('"$ref"'))
|
|
280
|
+
block(`${file}: cannot prove route "${key}" is disjoint from the new endpoints.`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const addedPolicyNames = new Set(plan.additions
|
|
284
|
+
.filter((a) => a.step.kind === "policy")
|
|
285
|
+
.map((a) => canonical(a.key)));
|
|
286
|
+
function checkDependency(name) {
|
|
287
|
+
const existing = byName.get(canonical(name));
|
|
288
|
+
if (existing &&
|
|
289
|
+
!profiles.some((p) => p.policies.some((policy) => isDeepStrictEqual(policy, existing)))) {
|
|
290
|
+
block(`${POLICIES}: required policy "${existing.name}" is customized; additions may change its behavior.`);
|
|
291
|
+
}
|
|
292
|
+
if (!existing && !addedPolicyNames.has(canonical(name)))
|
|
293
|
+
block(`${POLICIES}: required policy "${name}" is missing.`);
|
|
294
|
+
return existing;
|
|
295
|
+
}
|
|
296
|
+
for (const [key, pathItem] of Object.entries(oas.paths)) {
|
|
297
|
+
if (!targetOas.paths[key])
|
|
298
|
+
continue;
|
|
299
|
+
for (const [method, raw] of Object.entries(pathItem)) {
|
|
300
|
+
if (isMethodKey(method)) {
|
|
301
|
+
for (const name of operationSchema.parse(raw)["x-zuplo-route"]
|
|
302
|
+
.policies.inbound)
|
|
303
|
+
checkDependency(name);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
for (const addition of routeAdditions) {
|
|
308
|
+
const pathItem = z.record(z.string(), z.unknown()).parse(addition.value);
|
|
309
|
+
for (const [methods, raw] of Object.entries(pathItem)) {
|
|
310
|
+
if (!isMethodKey(methods))
|
|
311
|
+
continue;
|
|
312
|
+
const operation = operationSchema.parse(raw);
|
|
313
|
+
if (operationIds.has(operation.operationId))
|
|
314
|
+
block(`${AI_OAS}: operationId "${operation.operationId}" is already in use.`);
|
|
315
|
+
operationIds.add(operation.operationId);
|
|
316
|
+
operation["x-zuplo-route"].policies.inbound = operation["x-zuplo-route"].policies.inbound.map((name) => {
|
|
317
|
+
const existing = checkDependency(name);
|
|
318
|
+
return existing?.name ?? name;
|
|
319
|
+
});
|
|
320
|
+
pathItem[methods] = operation;
|
|
321
|
+
}
|
|
322
|
+
addition.value = pathItem;
|
|
323
|
+
}
|
|
324
|
+
for (const desired of targetPolicies.policies) {
|
|
325
|
+
const existing = byName.get(canonical(desired.name));
|
|
326
|
+
if (existing && !isDeepStrictEqual(existing, desired))
|
|
327
|
+
plan.notes.push(`${POLICIES}: existing "${existing.name}" differs from the target; preserved. Review its configuration separately.`);
|
|
328
|
+
}
|
|
329
|
+
let oasText = files[AI_OAS];
|
|
330
|
+
let policiesText = files[POLICIES];
|
|
331
|
+
for (const addition of plan.additions) {
|
|
332
|
+
if (addition.step.kind === "policy") {
|
|
333
|
+
policiesText = edit(policiesText, ["policies", -1], addition.value);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
const current = oasSchema.parse(readJson(oasText, AI_OAS));
|
|
337
|
+
const order = Object.keys(targetOas.paths);
|
|
338
|
+
const keys = Object.keys(current.paths);
|
|
339
|
+
const index = keys.findIndex((k) => order.indexOf(k) > order.indexOf(addition.key));
|
|
340
|
+
oasText = edit(oasText, ["paths", addition.key], addition.value, index < 0 ? keys.length : index);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
oasSchema.parse(readJson(oasText, AI_OAS));
|
|
344
|
+
policiesSchema.parse(readJson(policiesText, POLICIES));
|
|
345
|
+
if (policiesText !== files[POLICIES])
|
|
346
|
+
plan.after[POLICIES] = policiesText;
|
|
347
|
+
if (oasText !== files[AI_OAS])
|
|
348
|
+
plan.after[AI_OAS] = oasText;
|
|
349
|
+
const verifiedPresent = [...alreadyPresent].filter((id) => {
|
|
350
|
+
const step = data.steps.find((s) => s.id === id);
|
|
351
|
+
return step.kind === "route"
|
|
352
|
+
? profiles.some((p) => sameRoute(p.oas.paths[step.key], oas.paths[step.key]))
|
|
353
|
+
: profiles.some((p) => p.policies.some((policy) => isDeepStrictEqual(policy, byName.get(canonical(step.key)))));
|
|
354
|
+
});
|
|
355
|
+
plan.after[PROVENANCE] =
|
|
356
|
+
`${JSON.stringify({ ...provenance, appliedSteps: [...new Set([...provenance.appliedSteps, ...verifiedPresent, ...plan.additions.map((a) => a.step.id)])].sort() }, null, 2)}\n`;
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
block(error instanceof Error ? error.message : String(error));
|
|
360
|
+
}
|
|
361
|
+
if (plan.blockers.length)
|
|
362
|
+
plan.after = {};
|
|
363
|
+
return plan;
|
|
364
|
+
}
|
|
365
|
+
//# sourceMappingURL=planner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner.js","sourceRoot":"","sources":["../../../src/source/ai-gateway/planner.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { applyEdits, modify, type ParseError, parseTree } from \"jsonc-parser\";\nimport { z } from \"zod\";\nimport bundledCatalog from \"./catalog.json\" with { type: \"json\" };\n\nconst handlerSchema = z.looseObject({\n module: z.string(),\n export: z.string(),\n options: z.record(z.string(), z.unknown()).optional(),\n});\nconst policySchema = z.looseObject({\n name: z.string(),\n policyType: z.string().optional(),\n handler: handlerSchema,\n});\nconst policiesSchema = z.looseObject({ policies: z.array(policySchema) });\nconst oasSchema = z.looseObject({\n openapi: z.string(),\n paths: z.record(z.string(), z.record(z.string(), z.unknown())),\n});\nconst operationSchema = z.looseObject({\n operationId: z.string(),\n \"x-zuplo-route\": z.looseObject({\n handler: handlerSchema,\n policies: z.looseObject({ inbound: z.array(z.string()) }),\n }),\n});\nconst stepSchema = z.object({\n id: z.string(),\n kind: z.enum([\"route\", \"policy\"]),\n key: z.string(),\n target: z.string(),\n runtime: z.string(),\n instructions: z.string(),\n});\nexport const catalogSchema = z.object({\n formatVersion: z.literal(1),\n target: z.string(),\n aliases: z.record(z.string(), z.string()),\n snapshots: z.array(\n z.object({\n revision: z.string(),\n sourceCommit: z.string(),\n files: z.record(z.string(), z.string()),\n })\n ),\n steps: z.array(stepSchema),\n assessments: z.array(\n z.object({\n revision: z.string(),\n classification: z.enum([\"additions\", \"manual\", \"documentation-only\"]),\n instructions: z.string(),\n })\n ),\n});\nexport type Catalog = z.infer<typeof catalogSchema>;\nexport const catalog = catalogSchema.parse(bundledCatalog);\nexport const provenanceSchema = z\n .object({\n formatVersion: z.literal(1),\n template: z.literal(\"ai-gateway\"),\n origin: z.object({ revision: z.string(), release: z.string() }).nullable(),\n appliedSteps: z.array(z.string()),\n })\n .strict();\nexport const AI_OAS = \"config/ai.oas.json\";\nexport const POLICIES = \"config/policies.json\";\nexport const PROVENANCE = \".zuplo-template.json\";\nconst CATCH_ALL = \"/:app_id/(.*)\";\n// Match the build pipeline's method-key normalization, including multi-method keys.\nfunction isMethodKey(key: string): boolean {\n return key\n .split(\",\")\n .every((method) =>\n /^(get|post|put|patch|delete|head|options|trace)$/i.test(method.trim())\n );\n}\nexport type Files = Record<string, string>;\nexport interface Addition {\n step: z.infer<typeof stepSchema>;\n file: string;\n key: string;\n value: unknown;\n}\nexport interface UpgradePlan {\n target: string;\n release: string;\n blockers: string[];\n notes: string[];\n additions: Addition[];\n before: Files;\n after: Files;\n}\n\n/** Strict JSON/JSONC parsing, including duplicate keys (JSON.parse silently loses them). */\nexport function readJson(text: string, file: string): unknown {\n const errors: ParseError[] = [];\n const tree = parseTree(text, errors, {\n allowTrailingComma: file.endsWith(\".jsonc\"),\n disallowComments: !file.endsWith(\".jsonc\"),\n });\n if (!tree || errors.length)\n throw new Error(`${file}: malformed JSON; repair it before upgrading.`);\n function visit(node: NonNullable<typeof tree>) {\n if (node.type === \"object\") {\n const keys = new Set<string>();\n for (const property of node.children ?? []) {\n const key = property.children?.[0]?.value;\n if (typeof key !== \"string\" || keys.has(key)) {\n throw new Error(\n `${file}: duplicate or invalid key ${String(key)}; resolve it manually.`\n );\n }\n keys.add(key);\n }\n }\n for (const child of node.children ?? []) visit(child);\n }\n visit(tree);\n // getNodeValue would also work, but parsing through the tree keeps duplicate-key validation separate.\n function value(node: NonNullable<typeof tree>): unknown {\n if (node.type === \"object\")\n return Object.fromEntries(\n (node.children ?? []).map((p) => [\n p.children![0].value,\n value(p.children![1]),\n ])\n );\n if (node.type === \"array\") return (node.children ?? []).map(value);\n return node.value;\n }\n return value(tree);\n}\n\nexport function contentHash(text: string): string {\n return createHash(\"sha256\").update(text).digest(\"hex\");\n}\n\nfunction edit(\n text: string,\n pointer: (string | number)[],\n value: unknown,\n index?: number\n): string {\n const indent = text.match(/\\n([\\t ]+)\"/)?.[1] ?? \" \";\n return applyEdits(\n text,\n modify(text, pointer, value, {\n formattingOptions: {\n insertSpaces: !indent.includes(\"\\t\"),\n tabSize: indent.length,\n eol: text.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\",\n },\n ...(index === undefined ? {} : { getInsertionIndex: () => index }),\n })\n );\n}\n\n/** Pure planner: no network, filesystem writes, execution of customer code, or trusting metadata alone. */\nexport function planUpgrade(\n files: Files,\n release: string,\n data: Catalog = catalog\n): UpgradePlan {\n const plan: UpgradePlan = {\n target: data.target,\n release,\n blockers: [],\n notes: [],\n additions: [],\n before: { ...files },\n after: {},\n };\n const block = (message: string) => plan.blockers.push(message);\n try {\n const target = data.snapshots.find((s) => s.revision === data.target);\n if (!target)\n throw new Error(\n \"The bundled target snapshot is missing. Install a complete CLI release.\"\n );\n const targetOas = oasSchema.parse(readJson(target.files[AI_OAS], AI_OAS));\n const targetPolicies = policiesSchema.parse(\n readJson(target.files[POLICIES], POLICIES)\n );\n // Produce manual snippets even when customer input cannot be parsed.\n for (const step of data.steps.filter((s) => s.target === data.target)) {\n const entry =\n step.kind === \"route\"\n ? targetOas.paths[step.key]\n : targetPolicies.policies.find((p) => p.name === step.key);\n if (!entry)\n throw new Error(`Bundled upgrade ${step.id} has no definition.`);\n plan.additions.push({\n step,\n file: step.kind === \"route\" ? AI_OAS : POLICIES,\n key: step.key,\n value: entry,\n });\n }\n const oas = oasSchema.parse(readJson(files[AI_OAS], AI_OAS));\n const policies = policiesSchema.parse(readJson(files[POLICIES], POLICIES));\n const canonical = (name: string) => data.aliases[name] ?? name;\n // Added routes may retain a customer's historical policy declaration names.\n // Normalize only known policy references, never options, handlers, or chain order.\n function comparableRoute(value: unknown): unknown {\n if (value === undefined) return value;\n const route = z\n .record(z.string(), z.unknown())\n .parse(structuredClone(value));\n for (const [method, raw] of Object.entries(route)) {\n if (!isMethodKey(method)) continue;\n const result = operationSchema.safeParse(raw);\n if (result.success) {\n result.data[\"x-zuplo-route\"].policies.inbound =\n result.data[\"x-zuplo-route\"].policies.inbound.map(canonical);\n route[method] = result.data;\n }\n }\n return route;\n }\n const sameRoute = (a: unknown, b: unknown) =>\n isDeepStrictEqual(comparableRoute(a), comparableRoute(b));\n const byName = new Map<string, z.infer<typeof policySchema>>();\n for (const policy of policies.policies) {\n const name = canonical(policy.name);\n if (byName.has(name))\n block(\n `${POLICIES}: duplicate policy identity \"${name}\" (including historical aliases).`\n );\n byName.set(name, policy);\n }\n const profiles = data.snapshots.map((s) => ({\n snapshot: s,\n oas: oasSchema.parse(readJson(s.files[AI_OAS], AI_OAS)),\n policies: policiesSchema.parse(readJson(s.files[POLICIES], POLICIES))\n .policies,\n }));\n let provenance: z.infer<typeof provenanceSchema> = {\n formatVersion: 1,\n template: \"ai-gateway\",\n origin: null,\n appliedSteps: [],\n };\n if (files[PROVENANCE] !== undefined)\n provenance = provenanceSchema.parse(\n readJson(files[PROVENANCE], PROVENANCE)\n );\n if (\n provenance.origin &&\n !data.snapshots.some((s) => s.revision === provenance.origin?.revision)\n )\n block(\n `${PROVENANCE}: originating snapshot is unavailable; use manual instructions.`\n );\n if (\n provenance.appliedSteps.some((id) => !data.steps.some((s) => s.id === id))\n )\n block(\n `${PROVENANCE}: unknown upgrade step; use a CLI that supports this project.`\n );\n const original = profiles.find(\n (p) => p.snapshot.revision === provenance.origin?.revision\n );\n const alreadyPresent = new Set<string>();\n plan.additions = plan.additions.filter((addition) => {\n const present =\n addition.step.kind === \"route\"\n ? oas.paths[addition.key] !== undefined\n : byName.has(canonical(addition.key));\n if (present) alreadyPresent.add(addition.step.id);\n if (\n !present &&\n (provenance.appliedSteps.some((id) =>\n data.steps.some(\n (s) =>\n s.id === id &&\n s.kind === addition.step.kind &&\n (s.kind === \"policy\"\n ? canonical(s.key) === canonical(addition.step.key)\n : s.key === addition.step.key)\n )\n ) ||\n (original &&\n (addition.step.kind === \"route\"\n ? original.oas.paths[addition.key] !== undefined\n : original.policies.some(\n (p) => canonical(p.name) === canonical(addition.key)\n ))))\n ) {\n block(\n `${addition.file}: \"${addition.key}\" was previously present and is now missing; restore it manually if its removal was unintended.`\n );\n }\n return !present;\n });\n const originIndex = data.snapshots.findIndex(\n (s) => s.revision === provenance.origin?.revision\n );\n const targetIndex = data.snapshots.findIndex(\n (s) => s.revision === data.target\n );\n for (const assessment of data.assessments) {\n const index = data.snapshots.findIndex(\n (s) => s.revision === assessment.revision\n );\n if (index <= originIndex || index > targetIndex) continue;\n if (\n assessment.revision === data.target ||\n assessment.classification === \"manual\"\n )\n plan.notes.push(assessment.instructions);\n // Skipping a release must not skip its required manual migration.\n if (assessment.classification === \"manual\")\n block(assessment.instructions);\n }\n // No-op does not rewrite provenance or demand that unrelated customizations match the template.\n if (!plan.additions.length) return plan;\n\n const root = ({\n paths: _paths,\n info: _info,\n ...rest\n }: z.infer<typeof oasSchema>) => rest;\n if (!profiles.some((p) => isDeepStrictEqual(root(oas), root(p.oas))))\n block(\n `${AI_OAS}: customized document-level routing/security settings require manual review.`\n );\n const options = readJson(files[\"zuplo.jsonc\"], \"zuplo.jsonc\");\n if (\n !profiles.some((p) =>\n isDeepStrictEqual(\n options,\n readJson(p.snapshot.files[\"zuplo.jsonc\"], \"zuplo.jsonc\")\n )\n )\n )\n block(\n \"zuplo.jsonc: unrecognized configuration; verify routing and runtime extensions manually.\"\n );\n for (const [file, text] of Object.entries(files).filter(([f]) =>\n /(^|\\/)zuplo\\.runtime\\.[^/]+$/.test(f)\n )) {\n if (\n !data.snapshots.some(\n (s) =>\n s.files[file]?.replaceAll(\"\\r\\n\", \"\\n\") ===\n text.replaceAll(\"\\r\\n\", \"\\n\")\n )\n )\n block(\n `${file}: custom runtime extensions may affect routing; manual review required.`\n );\n }\n if (\n !oas.paths[CATCH_ALL] ||\n !profiles.some((p) =>\n sameRoute(p.oas.paths[CATCH_ALL], oas.paths[CATCH_ALL])\n )\n )\n block(\n `${AI_OAS}: the existing ${CATCH_ALL} route is not a supported template definition. New routes could bypass custom authentication.`\n );\n for (const [key, route] of Object.entries(oas.paths)) {\n if (\n targetOas.paths[key] &&\n !profiles.some((p) => sameRoute(p.oas.paths[key], route))\n )\n block(`${AI_OAS}: customized route \"${key}\" requires manual review.`);\n }\n const existingOrder = Object.keys(oas.paths).filter(\n (key) => targetOas.paths[key]\n );\n const expectedOrder = Object.keys(targetOas.paths).filter(\n (key) => oas.paths[key]\n );\n if (!isDeepStrictEqual(existingOrder, expectedOrder))\n block(\n `${AI_OAS}: existing gateway route precedence differs from the template; restore the intended order manually.`\n );\n const routeAdditions = plan.additions.filter(\n (a) => a.step.kind === \"route\"\n );\n const operationIds = new Set<string>();\n const addedPrefixes = routeAdditions.map((a) => a.key.split(\"/\")[1]);\n for (const [file, text] of Object.entries(files).filter(([f]) =>\n /^config\\/.*\\.oas\\.json$/.test(f)\n )) {\n const document = oasSchema.parse(readJson(text, file));\n for (const [key, pathItem] of Object.entries(document.paths)) {\n for (const [methods, operation] of Object.entries(pathItem)) {\n if (!isMethodKey(methods)) continue;\n const parsed = z\n .looseObject({ operationId: z.string().optional() })\n .parse(operation);\n if (parsed.operationId) {\n if (operationIds.has(parsed.operationId))\n block(`${file}: duplicate operationId \"${parsed.operationId}\".`);\n operationIds.add(parsed.operationId);\n }\n }\n if (!routeAdditions.length || (file === AI_OAS && targetOas.paths[key]))\n continue;\n // A distinct, unescaped literal first segment proves disjointness for /u/... and /a/....\n // Regex, parameters, escaped/encoded segments and references deliberately fail closed.\n const prefix = key.match(/^\\/([A-Za-z0-9_-]+)(?:\\/|$)/)?.[1];\n if (\n !prefix ||\n addedPrefixes.includes(prefix) ||\n JSON.stringify(pathItem).includes('\"$ref\"')\n )\n block(\n `${file}: cannot prove route \"${key}\" is disjoint from the new endpoints.`\n );\n }\n }\n const addedPolicyNames = new Set(\n plan.additions\n .filter((a) => a.step.kind === \"policy\")\n .map((a) => canonical(a.key))\n );\n function checkDependency(name: string) {\n const existing = byName.get(canonical(name));\n if (\n existing &&\n !profiles.some((p) =>\n p.policies.some((policy) => isDeepStrictEqual(policy, existing))\n )\n ) {\n block(\n `${POLICIES}: required policy \"${existing.name}\" is customized; additions may change its behavior.`\n );\n }\n if (!existing && !addedPolicyNames.has(canonical(name)))\n block(`${POLICIES}: required policy \"${name}\" is missing.`);\n return existing;\n }\n // Policy-only upgrades also run through the existing loader and executor.\n for (const [key, pathItem] of Object.entries(oas.paths)) {\n if (!targetOas.paths[key]) continue;\n for (const [method, raw] of Object.entries(pathItem)) {\n if (isMethodKey(method)) {\n for (const name of operationSchema.parse(raw)[\"x-zuplo-route\"]\n .policies.inbound)\n checkDependency(name);\n }\n }\n }\n for (const addition of routeAdditions) {\n const pathItem = z.record(z.string(), z.unknown()).parse(addition.value);\n for (const [methods, raw] of Object.entries(pathItem)) {\n if (!isMethodKey(methods)) continue;\n const operation = operationSchema.parse(raw);\n if (operationIds.has(operation.operationId))\n block(\n `${AI_OAS}: operationId \"${operation.operationId}\" is already in use.`\n );\n operationIds.add(operation.operationId);\n operation[\"x-zuplo-route\"].policies.inbound = operation[\n \"x-zuplo-route\"\n ].policies.inbound.map((name) => {\n const existing = checkDependency(name);\n return existing?.name ?? name;\n });\n pathItem[methods] = operation;\n }\n addition.value = pathItem;\n }\n // Report changed definitions without proposing replacements or claiming complete template parity.\n for (const desired of targetPolicies.policies) {\n const existing = byName.get(canonical(desired.name));\n if (existing && !isDeepStrictEqual(existing, desired))\n plan.notes.push(\n `${POLICIES}: existing \"${existing.name}\" differs from the target; preserved. Review its configuration separately.`\n );\n }\n let oasText = files[AI_OAS];\n let policiesText = files[POLICIES];\n for (const addition of plan.additions) {\n if (addition.step.kind === \"policy\") {\n policiesText = edit(policiesText, [\"policies\", -1], addition.value);\n } else {\n const current = oasSchema.parse(readJson(oasText, AI_OAS));\n const order = Object.keys(targetOas.paths);\n const keys = Object.keys(current.paths);\n const index = keys.findIndex(\n (k) => order.indexOf(k) > order.indexOf(addition.key)\n );\n oasText = edit(\n oasText,\n [\"paths\", addition.key],\n addition.value,\n index < 0 ? keys.length : index\n );\n }\n }\n // Validate staged syntax and schema before exposing a writable plan.\n oasSchema.parse(readJson(oasText, AI_OAS));\n policiesSchema.parse(readJson(policiesText, POLICIES));\n // Install declarations before routes that reference them; provenance is always last.\n if (policiesText !== files[POLICIES]) plan.after[POLICIES] = policiesText;\n if (oasText !== files[AI_OAS]) plan.after[AI_OAS] = oasText;\n const verifiedPresent = [...alreadyPresent].filter((id) => {\n const step = data.steps.find((s) => s.id === id)!;\n return step.kind === \"route\"\n ? profiles.some((p) =>\n sameRoute(p.oas.paths[step.key], oas.paths[step.key])\n )\n : profiles.some((p) =>\n p.policies.some((policy) =>\n isDeepStrictEqual(policy, byName.get(canonical(step.key)))\n )\n );\n });\n plan.after[PROVENANCE] =\n `${JSON.stringify({ ...provenance, appliedSteps: [...new Set([...provenance.appliedSteps, ...verifiedPresent, ...plan.additions.map((a) => a.step.id)])].sort() }, null, 2)}\\n`;\n } catch (error) {\n block(error instanceof Error ? error.message : String(error));\n }\n if (plan.blockers.length) plan.after = {};\n return plan;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner.test.d.ts","sourceRoot":"","sources":["../../../src/source/ai-gateway/planner.test.ts"],"names":[],"mappings":""}
|