@quonfig/cli 0.0.22 → 0.0.24
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 +36 -36
- package/dist/commands/log-level.js +1 -3
- package/dist/commands/login.js +30 -1
- package/dist/commands/pull.js +2 -2
- package/dist/commands/push.d.ts +6 -14
- package/dist/commands/push.js +54 -99
- package/dist/commands/workspace/switch.js +1 -4
- package/dist/init/schema.js +4 -2
- package/dist/migrate/push-to-cloud.js +3 -6
- package/dist/migrate/sources/launch/zod-to-json-schema.js +130 -132
- package/dist/migrate/sources/launch.js +1 -1
- package/dist/push/bare-path-diff.js +19 -17
- package/dist/push/config-push-client.d.ts +18 -0
- package/dist/push/config-push-client.js +82 -0
- package/dist/push/diff-summary.d.ts +11 -0
- package/dist/push/diff-summary.js +1 -8
- package/dist/push/run-push.d.ts +53 -13
- package/dist/push/run-push.js +44 -132
- package/dist/util/gitea-token-storage.js +2 -2
- package/dist/util/open-browser.d.ts +1 -0
- package/dist/util/open-browser.js +27 -0
- package/dist/util/quonfig-home.d.ts +6 -0
- package/dist/util/quonfig-home.js +8 -0
- package/dist/util/resolve-workspace.js +6 -2
- package/dist/util/token-storage.js +2 -2
- package/dist/verify/validate.js +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/dist/init/schema.js
CHANGED
|
@@ -298,8 +298,10 @@ export function storedConfigJsonSchema() {
|
|
|
298
298
|
type: 'string',
|
|
299
299
|
description: 'References a schema in schemas/ or schemas-protected/. The referenced file must exist.',
|
|
300
300
|
},
|
|
301
|
-
|
|
302
|
-
|
|
301
|
+
access: {
|
|
302
|
+
enum: ['support', 'standard', 'protected-env', 'protected-all-envs'],
|
|
303
|
+
description: 'Edit-access tier for this config. Defaults to "standard" when absent. See protecting-access.md.',
|
|
304
|
+
},
|
|
303
305
|
tags: {
|
|
304
306
|
type: 'array',
|
|
305
307
|
items: { type: 'string' },
|
|
@@ -3,7 +3,7 @@ import * as path from 'node:path';
|
|
|
3
3
|
import { removeQfFromGitignore, writeImportState } from './import-state.js';
|
|
4
4
|
import { writeMigrationReport } from './migration-report.js';
|
|
5
5
|
import { detectDuplicateKeys } from './sources/launch/translate.js';
|
|
6
|
-
import { cloneAndStackPush } from '../util/clone-and-stack-push.js';
|
|
6
|
+
import { cloneAndStackPush, } from '../util/clone-and-stack-push.js';
|
|
7
7
|
const writeQuonfigFiles = (dir, changes, source) => {
|
|
8
8
|
const livePaths = new Map();
|
|
9
9
|
for (const change of changes) {
|
|
@@ -59,8 +59,7 @@ const mergeEnvironmentsIntoQuonfigJson = (dir, sourceEnvs) => {
|
|
|
59
59
|
}
|
|
60
60
|
const existingEnvs = Array.isArray(existing.environments) ? existing.environments : [];
|
|
61
61
|
const merged = [...new Set([...existingEnvs, ...sourceEnvs])].sort();
|
|
62
|
-
if (existingEnvs.length === merged.length &&
|
|
63
|
-
existingEnvs.every((e, i) => e === merged[i])) {
|
|
62
|
+
if (existingEnvs.length === merged.length && existingEnvs.every((e, i) => e === merged[i])) {
|
|
64
63
|
return;
|
|
65
64
|
}
|
|
66
65
|
fs.writeFileSync(quonfigPath, JSON.stringify({ ...existing, environments: merged }, null, 2) + '\n', 'utf8');
|
|
@@ -80,9 +79,7 @@ export const pushMigrationToCloud = async (opts) => {
|
|
|
80
79
|
droppedOverrides = opts.source.getDroppedOverrides?.() ?? null;
|
|
81
80
|
skippedConfigs = opts.source.getSkippedConfigs?.() ?? null;
|
|
82
81
|
duplicateResolutions =
|
|
83
|
-
resolutionEntries.length > 0
|
|
84
|
-
? { entries: resolutionEntries, total: resolutionEntries.length }
|
|
85
|
-
: null;
|
|
82
|
+
resolutionEntries.length > 0 ? { entries: resolutionEntries, total: resolutionEntries.length } : null;
|
|
86
83
|
const reportData = {
|
|
87
84
|
...opts.reportData,
|
|
88
85
|
...(droppedOverrides ? { droppedOverrides } : {}),
|
|
@@ -2,27 +2,25 @@
|
|
|
2
2
|
// Vendored verbatim from launch-migrator/src/zod-to-json-schema.ts to keep a
|
|
3
3
|
// drop-in sync path. Do not reformat — edits here should be mirrored against
|
|
4
4
|
// launch-migrator so the two converters stay behaviorally identical.
|
|
5
|
-
import * as ts from
|
|
6
|
-
const JSON_SCHEMA_DRAFT_2020_12 =
|
|
5
|
+
import * as ts from 'typescript';
|
|
6
|
+
const JSON_SCHEMA_DRAFT_2020_12 = 'https://json-schema.org/draft/2020-12/schema';
|
|
7
7
|
function unwrapExpression(node) {
|
|
8
8
|
let current = node;
|
|
9
|
-
while (ts.isParenthesizedExpression(current) ||
|
|
10
|
-
ts.isAsExpression(current) ||
|
|
11
|
-
ts.isTypeAssertionExpression(current)) {
|
|
9
|
+
while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isTypeAssertionExpression(current)) {
|
|
12
10
|
current = current.expression;
|
|
13
11
|
}
|
|
14
12
|
return current;
|
|
15
13
|
}
|
|
16
14
|
function parseRegexLiteral(text) {
|
|
17
|
-
if (!text.startsWith(
|
|
15
|
+
if (!text.startsWith('/')) {
|
|
18
16
|
throw new Error(`Expected regex literal, got: ${text}`);
|
|
19
17
|
}
|
|
20
18
|
let lastSlash = -1;
|
|
21
19
|
for (let index = text.length - 1; index > 0; index -= 1) {
|
|
22
|
-
if (text[index] !==
|
|
20
|
+
if (text[index] !== '/')
|
|
23
21
|
continue;
|
|
24
22
|
let backslashCount = 0;
|
|
25
|
-
for (let i = index - 1; i >= 0 && text[i] ===
|
|
23
|
+
for (let i = index - 1; i >= 0 && text[i] === '\\'; i -= 1) {
|
|
26
24
|
backslashCount += 1;
|
|
27
25
|
}
|
|
28
26
|
if (backslashCount % 2 === 0) {
|
|
@@ -39,7 +37,7 @@ function parseRegexLiteral(text) {
|
|
|
39
37
|
};
|
|
40
38
|
}
|
|
41
39
|
function isPlainObject(value) {
|
|
42
|
-
return typeof value ===
|
|
40
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
43
41
|
}
|
|
44
42
|
function evaluateLiteralExpression(node) {
|
|
45
43
|
const current = unwrapExpression(node);
|
|
@@ -57,13 +55,13 @@ function evaluateLiteralExpression(node) {
|
|
|
57
55
|
return null;
|
|
58
56
|
if (ts.isPrefixUnaryExpression(current) && current.operator === ts.SyntaxKind.MinusToken) {
|
|
59
57
|
const operand = evaluateLiteralExpression(current.operand);
|
|
60
|
-
if (typeof operand ===
|
|
58
|
+
if (typeof operand === 'number')
|
|
61
59
|
return -operand;
|
|
62
60
|
}
|
|
63
61
|
if (ts.isArrayLiteralExpression(current)) {
|
|
64
62
|
return current.elements.map((element) => {
|
|
65
63
|
if (ts.isSpreadElement(element)) {
|
|
66
|
-
throw new Error(
|
|
64
|
+
throw new Error('Spread elements are not supported in literal arrays');
|
|
67
65
|
}
|
|
68
66
|
return evaluateLiteralExpression(element);
|
|
69
67
|
});
|
|
@@ -72,10 +70,10 @@ function evaluateLiteralExpression(node) {
|
|
|
72
70
|
const result = {};
|
|
73
71
|
for (const property of current.properties) {
|
|
74
72
|
if (ts.isSpreadAssignment(property) || ts.isShorthandPropertyAssignment(property)) {
|
|
75
|
-
throw new Error(
|
|
73
|
+
throw new Error('Spread and shorthand properties are not supported in literal objects');
|
|
76
74
|
}
|
|
77
75
|
if (!ts.isPropertyAssignment(property)) {
|
|
78
|
-
throw new Error(
|
|
76
|
+
throw new Error('Unsupported object literal property');
|
|
79
77
|
}
|
|
80
78
|
const key = getPropertyName(property.name);
|
|
81
79
|
result[key] = evaluateLiteralExpression(property.initializer);
|
|
@@ -88,14 +86,14 @@ function evaluateLiteralExpression(node) {
|
|
|
88
86
|
if (ts.isBinaryExpression(current) && current.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
89
87
|
const left = evaluateLiteralExpression(current.left);
|
|
90
88
|
const right = evaluateLiteralExpression(current.right);
|
|
91
|
-
if (typeof left ===
|
|
89
|
+
if (typeof left === 'string' || typeof right === 'string') {
|
|
92
90
|
return String(left) + String(right);
|
|
93
91
|
}
|
|
94
|
-
if (typeof left ===
|
|
92
|
+
if (typeof left === 'number' && typeof right === 'number') {
|
|
95
93
|
return left + right;
|
|
96
94
|
}
|
|
97
95
|
}
|
|
98
|
-
if (ts.isIdentifier(current) && current.text ===
|
|
96
|
+
if (ts.isIdentifier(current) && current.text === 'undefined') {
|
|
99
97
|
return undefined;
|
|
100
98
|
}
|
|
101
99
|
throw new Error(`Unsupported literal expression: ${current.getText()}`);
|
|
@@ -108,38 +106,38 @@ function getPropertyName(name) {
|
|
|
108
106
|
}
|
|
109
107
|
function schemaKind(schema) {
|
|
110
108
|
if (schema === false)
|
|
111
|
-
return
|
|
109
|
+
return 'never';
|
|
112
110
|
if (!isPlainObject(schema))
|
|
113
|
-
return
|
|
111
|
+
return 'unknown';
|
|
114
112
|
if (Array.isArray(schema.anyOf))
|
|
115
|
-
return
|
|
113
|
+
return 'union';
|
|
116
114
|
if (schema.const !== undefined)
|
|
117
|
-
return
|
|
115
|
+
return 'const';
|
|
118
116
|
if (Array.isArray(schema.enum))
|
|
119
|
-
return
|
|
117
|
+
return 'enum';
|
|
120
118
|
const type = schema.type;
|
|
121
|
-
if (typeof type ===
|
|
119
|
+
if (typeof type === 'string')
|
|
122
120
|
return type;
|
|
123
|
-
return
|
|
121
|
+
return 'unknown';
|
|
124
122
|
}
|
|
125
123
|
function ensureObjectSchema(schema) {
|
|
126
|
-
if (schema === false || !isPlainObject(schema) || schema.type !==
|
|
127
|
-
throw new Error(
|
|
124
|
+
if (schema === false || !isPlainObject(schema) || schema.type !== 'object') {
|
|
125
|
+
throw new Error('Expected object schema');
|
|
128
126
|
}
|
|
129
127
|
return schema;
|
|
130
128
|
}
|
|
131
129
|
function makeNullable(schema) {
|
|
132
130
|
if (!isPlainObject(schema)) {
|
|
133
|
-
return { anyOf: [schema, { type:
|
|
131
|
+
return { anyOf: [schema, { type: 'null' }] };
|
|
134
132
|
}
|
|
135
133
|
const existing = schema.type;
|
|
136
134
|
if (Array.isArray(existing)) {
|
|
137
|
-
return existing.includes(
|
|
135
|
+
return existing.includes('null') ? schema : { ...schema, type: [...existing, 'null'] };
|
|
138
136
|
}
|
|
139
|
-
if (typeof existing ===
|
|
140
|
-
return { ...schema, type: [existing,
|
|
137
|
+
if (typeof existing === 'string') {
|
|
138
|
+
return { ...schema, type: [existing, 'null'] };
|
|
141
139
|
}
|
|
142
|
-
return { anyOf: [schema, { type:
|
|
140
|
+
return { anyOf: [schema, { type: 'null' }] };
|
|
143
141
|
}
|
|
144
142
|
function withAnnotation(schema, key, value) {
|
|
145
143
|
if (schema === false) {
|
|
@@ -153,24 +151,24 @@ function applyNumericConstraint(schema, kind, method, value) {
|
|
|
153
151
|
if (schema === false)
|
|
154
152
|
return schema;
|
|
155
153
|
const next = { ...schema };
|
|
156
|
-
if (kind ===
|
|
157
|
-
if (method ===
|
|
154
|
+
if (kind === 'number' || kind === 'integer') {
|
|
155
|
+
if (method === 'min' || method === 'length')
|
|
158
156
|
next.minimum = value;
|
|
159
|
-
if (method ===
|
|
157
|
+
if (method === 'max' || method === 'length')
|
|
160
158
|
next.maximum = value;
|
|
161
159
|
return next;
|
|
162
160
|
}
|
|
163
|
-
if (kind ===
|
|
164
|
-
if (method ===
|
|
161
|
+
if (kind === 'string') {
|
|
162
|
+
if (method === 'min' || method === 'length')
|
|
165
163
|
next.minLength = value;
|
|
166
|
-
if (method ===
|
|
164
|
+
if (method === 'max' || method === 'length')
|
|
167
165
|
next.maxLength = value;
|
|
168
166
|
return next;
|
|
169
167
|
}
|
|
170
|
-
if (kind ===
|
|
171
|
-
if (method ===
|
|
168
|
+
if (kind === 'array') {
|
|
169
|
+
if (method === 'min' || method === 'length')
|
|
172
170
|
next.minItems = value;
|
|
173
|
-
if (method ===
|
|
171
|
+
if (method === 'max' || method === 'length')
|
|
174
172
|
next.maxItems = value;
|
|
175
173
|
return next;
|
|
176
174
|
}
|
|
@@ -179,35 +177,35 @@ function applyNumericConstraint(schema, kind, method, value) {
|
|
|
179
177
|
function applyMethod(receiver, method, args, ctx) {
|
|
180
178
|
const kind = schemaKind(receiver.schema);
|
|
181
179
|
switch (method) {
|
|
182
|
-
case
|
|
180
|
+
case 'optional':
|
|
183
181
|
return { ...receiver, optional: true };
|
|
184
|
-
case
|
|
182
|
+
case 'nullable':
|
|
185
183
|
return { schema: makeNullable(receiver.schema), optional: receiver.optional };
|
|
186
|
-
case
|
|
184
|
+
case 'default': {
|
|
187
185
|
if (args.length === 0)
|
|
188
|
-
throw new Error(
|
|
186
|
+
throw new Error('default() requires an argument');
|
|
189
187
|
return {
|
|
190
|
-
schema: withAnnotation(receiver.schema,
|
|
188
|
+
schema: withAnnotation(receiver.schema, 'default', evaluateLiteralExpression(args[0])),
|
|
191
189
|
optional: true,
|
|
192
190
|
};
|
|
193
191
|
}
|
|
194
|
-
case
|
|
192
|
+
case 'describe': {
|
|
195
193
|
if (args.length === 0)
|
|
196
|
-
throw new Error(
|
|
194
|
+
throw new Error('describe() requires an argument');
|
|
197
195
|
return {
|
|
198
|
-
schema: withAnnotation(receiver.schema,
|
|
196
|
+
schema: withAnnotation(receiver.schema, 'description', evaluateLiteralExpression(args[0])),
|
|
199
197
|
optional: receiver.optional,
|
|
200
198
|
};
|
|
201
199
|
}
|
|
202
|
-
case
|
|
200
|
+
case 'meta': {
|
|
203
201
|
if (args.length === 0)
|
|
204
|
-
throw new Error(
|
|
202
|
+
throw new Error('meta() requires an argument');
|
|
205
203
|
const meta = evaluateLiteralExpression(args[0]);
|
|
206
204
|
if (!isPlainObject(meta)) {
|
|
207
|
-
throw new Error(
|
|
205
|
+
throw new Error('meta() expects an object literal');
|
|
208
206
|
}
|
|
209
207
|
let schema = receiver.schema;
|
|
210
|
-
const supportedKeys = new Set([
|
|
208
|
+
const supportedKeys = new Set(['description', 'title']);
|
|
211
209
|
for (const [key, value] of Object.entries(meta)) {
|
|
212
210
|
if (supportedKeys.has(key)) {
|
|
213
211
|
schema = withAnnotation(schema, key, value);
|
|
@@ -218,99 +216,99 @@ function applyMethod(receiver, method, args, ctx) {
|
|
|
218
216
|
}
|
|
219
217
|
return { schema, optional: receiver.optional };
|
|
220
218
|
}
|
|
221
|
-
case
|
|
222
|
-
if (kind !==
|
|
223
|
-
throw new Error(
|
|
219
|
+
case 'url':
|
|
220
|
+
if (kind !== 'string') {
|
|
221
|
+
throw new Error('url() can only be applied to string schemas');
|
|
224
222
|
}
|
|
225
223
|
return {
|
|
226
|
-
schema: withAnnotation(receiver.schema,
|
|
224
|
+
schema: withAnnotation(receiver.schema, 'format', 'uri'),
|
|
227
225
|
optional: receiver.optional,
|
|
228
226
|
};
|
|
229
|
-
case
|
|
230
|
-
if (kind !==
|
|
231
|
-
throw new Error(
|
|
227
|
+
case 'email':
|
|
228
|
+
if (kind !== 'string') {
|
|
229
|
+
throw new Error('email() can only be applied to string schemas');
|
|
232
230
|
}
|
|
233
231
|
return {
|
|
234
|
-
schema: withAnnotation(receiver.schema,
|
|
232
|
+
schema: withAnnotation(receiver.schema, 'format', 'email'),
|
|
235
233
|
optional: receiver.optional,
|
|
236
234
|
};
|
|
237
|
-
case
|
|
238
|
-
if (kind !==
|
|
239
|
-
throw new Error(
|
|
235
|
+
case 'uuid':
|
|
236
|
+
if (kind !== 'string') {
|
|
237
|
+
throw new Error('uuid() can only be applied to string schemas');
|
|
240
238
|
}
|
|
241
239
|
return {
|
|
242
|
-
schema: withAnnotation(receiver.schema,
|
|
240
|
+
schema: withAnnotation(receiver.schema, 'format', 'uuid'),
|
|
243
241
|
optional: receiver.optional,
|
|
244
242
|
};
|
|
245
|
-
case
|
|
246
|
-
if (kind !==
|
|
247
|
-
throw new Error(
|
|
243
|
+
case 'datetime':
|
|
244
|
+
if (kind !== 'string') {
|
|
245
|
+
throw new Error('datetime() can only be applied to string schemas');
|
|
248
246
|
}
|
|
249
247
|
return {
|
|
250
|
-
schema: withAnnotation(receiver.schema,
|
|
248
|
+
schema: withAnnotation(receiver.schema, 'format', 'date-time'),
|
|
251
249
|
optional: receiver.optional,
|
|
252
250
|
};
|
|
253
|
-
case
|
|
254
|
-
if (kind !==
|
|
255
|
-
throw new Error(
|
|
251
|
+
case 'regex': {
|
|
252
|
+
if (kind !== 'string') {
|
|
253
|
+
throw new Error('regex() can only be applied to string schemas');
|
|
256
254
|
}
|
|
257
255
|
if (args.length === 0)
|
|
258
|
-
throw new Error(
|
|
256
|
+
throw new Error('regex() requires an argument');
|
|
259
257
|
const regex = evaluateLiteralExpression(args[0]);
|
|
260
|
-
if (!isPlainObject(regex) || typeof regex.pattern !==
|
|
261
|
-
throw new Error(
|
|
258
|
+
if (!isPlainObject(regex) || typeof regex.pattern !== 'string') {
|
|
259
|
+
throw new Error('regex() expects a regex literal');
|
|
262
260
|
}
|
|
263
|
-
if (typeof regex.flags ===
|
|
261
|
+
if (typeof regex.flags === 'string' && regex.flags.length > 0) {
|
|
264
262
|
ctx.warnings.push(`Ignored regex flags "${regex.flags}"`);
|
|
265
263
|
}
|
|
266
264
|
return {
|
|
267
|
-
schema: withAnnotation(receiver.schema,
|
|
265
|
+
schema: withAnnotation(receiver.schema, 'pattern', regex.pattern),
|
|
268
266
|
optional: receiver.optional,
|
|
269
267
|
};
|
|
270
268
|
}
|
|
271
|
-
case
|
|
269
|
+
case 'min':
|
|
272
270
|
if (args.length === 0)
|
|
273
|
-
throw new Error(
|
|
271
|
+
throw new Error('min() requires an argument');
|
|
274
272
|
return {
|
|
275
|
-
schema: applyNumericConstraint(receiver.schema, kind,
|
|
273
|
+
schema: applyNumericConstraint(receiver.schema, kind, 'min', Number(evaluateLiteralExpression(args[0]))),
|
|
276
274
|
optional: receiver.optional,
|
|
277
275
|
};
|
|
278
|
-
case
|
|
276
|
+
case 'max':
|
|
279
277
|
if (args.length === 0)
|
|
280
|
-
throw new Error(
|
|
278
|
+
throw new Error('max() requires an argument');
|
|
281
279
|
return {
|
|
282
|
-
schema: applyNumericConstraint(receiver.schema, kind,
|
|
280
|
+
schema: applyNumericConstraint(receiver.schema, kind, 'max', Number(evaluateLiteralExpression(args[0]))),
|
|
283
281
|
optional: receiver.optional,
|
|
284
282
|
};
|
|
285
|
-
case
|
|
283
|
+
case 'length':
|
|
286
284
|
if (args.length === 0)
|
|
287
|
-
throw new Error(
|
|
285
|
+
throw new Error('length() requires an argument');
|
|
288
286
|
return {
|
|
289
|
-
schema: applyNumericConstraint(receiver.schema, kind,
|
|
287
|
+
schema: applyNumericConstraint(receiver.schema, kind, 'length', Number(evaluateLiteralExpression(args[0]))),
|
|
290
288
|
optional: receiver.optional,
|
|
291
289
|
};
|
|
292
|
-
case
|
|
293
|
-
if (kind !==
|
|
294
|
-
throw new Error(
|
|
290
|
+
case 'int': {
|
|
291
|
+
if (kind !== 'number' && kind !== 'integer') {
|
|
292
|
+
throw new Error('int() can only be applied to number schemas');
|
|
295
293
|
}
|
|
296
|
-
const next = withAnnotation(receiver.schema,
|
|
294
|
+
const next = withAnnotation(receiver.schema, 'type', 'integer');
|
|
297
295
|
return { schema: next, optional: receiver.optional };
|
|
298
296
|
}
|
|
299
|
-
case
|
|
300
|
-
if (kind !==
|
|
301
|
-
throw new Error(
|
|
297
|
+
case 'positive': {
|
|
298
|
+
if (kind !== 'number' && kind !== 'integer') {
|
|
299
|
+
throw new Error('positive() can only be applied to number schemas');
|
|
302
300
|
}
|
|
303
|
-
const next = withAnnotation(receiver.schema,
|
|
301
|
+
const next = withAnnotation(receiver.schema, 'exclusiveMinimum', 0);
|
|
304
302
|
return { schema: next, optional: receiver.optional };
|
|
305
303
|
}
|
|
306
|
-
case
|
|
304
|
+
case 'strict': {
|
|
307
305
|
const objectSchema = ensureObjectSchema(receiver.schema);
|
|
308
306
|
return {
|
|
309
307
|
schema: { ...objectSchema, additionalProperties: false },
|
|
310
308
|
optional: receiver.optional,
|
|
311
309
|
};
|
|
312
310
|
}
|
|
313
|
-
case
|
|
311
|
+
case 'partial': {
|
|
314
312
|
// .partial() makes all declared properties optional. For records
|
|
315
313
|
// (open-shape objects) there's nothing to do.
|
|
316
314
|
if (!isPlainObject(receiver.schema)) {
|
|
@@ -319,17 +317,17 @@ function applyMethod(receiver, method, args, ctx) {
|
|
|
319
317
|
const { required: _required, ...rest } = receiver.schema;
|
|
320
318
|
return { schema: rest, optional: receiver.optional };
|
|
321
319
|
}
|
|
322
|
-
case
|
|
320
|
+
case 'array':
|
|
323
321
|
return {
|
|
324
322
|
schema: {
|
|
325
|
-
type:
|
|
323
|
+
type: 'array',
|
|
326
324
|
items: receiver.schema,
|
|
327
325
|
},
|
|
328
326
|
optional: receiver.optional,
|
|
329
327
|
};
|
|
330
|
-
case
|
|
328
|
+
case 'or': {
|
|
331
329
|
if (args.length === 0)
|
|
332
|
-
throw new Error(
|
|
330
|
+
throw new Error('or() requires an argument');
|
|
333
331
|
const other = convertExpression(args[0], ctx);
|
|
334
332
|
return {
|
|
335
333
|
schema: { anyOf: [receiver.schema, other.schema] },
|
|
@@ -345,7 +343,7 @@ function convertObjectShape(objectLiteral, ctx) {
|
|
|
345
343
|
const required = [];
|
|
346
344
|
for (const property of objectLiteral.properties) {
|
|
347
345
|
if (!ts.isPropertyAssignment(property)) {
|
|
348
|
-
throw new Error(
|
|
346
|
+
throw new Error('Only property assignments are supported in z.object()');
|
|
349
347
|
}
|
|
350
348
|
const key = getPropertyName(property.name);
|
|
351
349
|
const child = convertExpression(property.initializer, ctx);
|
|
@@ -355,20 +353,20 @@ function convertObjectShape(objectLiteral, ctx) {
|
|
|
355
353
|
}
|
|
356
354
|
}
|
|
357
355
|
return {
|
|
358
|
-
type:
|
|
356
|
+
type: 'object',
|
|
359
357
|
properties,
|
|
360
358
|
required,
|
|
361
359
|
};
|
|
362
360
|
}
|
|
363
361
|
function convertBaseZodCall(method, args, ctx) {
|
|
364
362
|
switch (method) {
|
|
365
|
-
case
|
|
366
|
-
return { schema: { type:
|
|
367
|
-
case
|
|
368
|
-
return { schema: { type:
|
|
369
|
-
case
|
|
370
|
-
return { schema: { type:
|
|
371
|
-
case
|
|
363
|
+
case 'string':
|
|
364
|
+
return { schema: { type: 'string' }, optional: false };
|
|
365
|
+
case 'number':
|
|
366
|
+
return { schema: { type: 'number' }, optional: false };
|
|
367
|
+
case 'boolean':
|
|
368
|
+
return { schema: { type: 'boolean' }, optional: false };
|
|
369
|
+
case 'enum': {
|
|
372
370
|
// Canonical form: z.enum(["a", "b"]). Also tolerate the hand-written
|
|
373
371
|
// typo z.enum("a", "b") by treating variadic args as an implicit array.
|
|
374
372
|
let elements;
|
|
@@ -379,46 +377,46 @@ function convertBaseZodCall(method, args, ctx) {
|
|
|
379
377
|
elements = args;
|
|
380
378
|
}
|
|
381
379
|
else {
|
|
382
|
-
throw new Error(
|
|
380
|
+
throw new Error('enum() expects at least one argument');
|
|
383
381
|
}
|
|
384
382
|
const values = elements.map((element) => evaluateLiteralExpression(element));
|
|
385
383
|
return { schema: { enum: values }, optional: false };
|
|
386
384
|
}
|
|
387
|
-
case
|
|
385
|
+
case 'literal': {
|
|
388
386
|
if (args.length !== 1)
|
|
389
|
-
throw new Error(
|
|
387
|
+
throw new Error('literal() expects a single argument');
|
|
390
388
|
return { schema: { const: evaluateLiteralExpression(args[0]) }, optional: false };
|
|
391
389
|
}
|
|
392
|
-
case
|
|
390
|
+
case 'never':
|
|
393
391
|
return { schema: false, optional: false };
|
|
394
|
-
case
|
|
392
|
+
case 'array': {
|
|
395
393
|
if (args.length !== 1) {
|
|
396
|
-
throw new Error(
|
|
394
|
+
throw new Error('array() expects a single argument');
|
|
397
395
|
}
|
|
398
396
|
const item = convertExpression(args[0], ctx);
|
|
399
397
|
return {
|
|
400
398
|
schema: {
|
|
401
|
-
type:
|
|
399
|
+
type: 'array',
|
|
402
400
|
items: item.schema,
|
|
403
401
|
},
|
|
404
402
|
optional: false,
|
|
405
403
|
};
|
|
406
404
|
}
|
|
407
|
-
case
|
|
405
|
+
case 'object': {
|
|
408
406
|
if (args.length === 0) {
|
|
409
|
-
return { schema: { type:
|
|
407
|
+
return { schema: { type: 'object' }, optional: false };
|
|
410
408
|
}
|
|
411
409
|
if (args.length !== 1 || !ts.isObjectLiteralExpression(args[0])) {
|
|
412
|
-
throw new Error(
|
|
410
|
+
throw new Error('object() expects a single object literal argument');
|
|
413
411
|
}
|
|
414
412
|
return {
|
|
415
413
|
schema: convertObjectShape(args[0], ctx),
|
|
416
414
|
optional: false,
|
|
417
415
|
};
|
|
418
416
|
}
|
|
419
|
-
case
|
|
417
|
+
case 'record': {
|
|
420
418
|
if (args.length === 0 || args.length > 2) {
|
|
421
|
-
throw new Error(
|
|
419
|
+
throw new Error('record() expects one or two arguments');
|
|
422
420
|
}
|
|
423
421
|
let keySchema;
|
|
424
422
|
let valueIndex = 0;
|
|
@@ -428,7 +426,7 @@ function convertBaseZodCall(method, args, ctx) {
|
|
|
428
426
|
}
|
|
429
427
|
const valueSchema = convertExpression(args[valueIndex], ctx);
|
|
430
428
|
const schema = {
|
|
431
|
-
type:
|
|
429
|
+
type: 'object',
|
|
432
430
|
additionalProperties: valueSchema.schema,
|
|
433
431
|
};
|
|
434
432
|
if (keySchema && isPlainObject(keySchema.schema) && Array.isArray(keySchema.schema.enum)) {
|
|
@@ -436,19 +434,19 @@ function convertBaseZodCall(method, args, ctx) {
|
|
|
436
434
|
}
|
|
437
435
|
return { schema, optional: false };
|
|
438
436
|
}
|
|
439
|
-
case
|
|
437
|
+
case 'union': {
|
|
440
438
|
if (args.length !== 1 || !ts.isArrayLiteralExpression(args[0])) {
|
|
441
|
-
throw new Error(
|
|
439
|
+
throw new Error('union() expects a single array literal argument');
|
|
442
440
|
}
|
|
443
441
|
const options = args[0].elements.map((element) => convertExpression(element, ctx).schema);
|
|
444
442
|
return { schema: { anyOf: options }, optional: false };
|
|
445
443
|
}
|
|
446
|
-
case
|
|
444
|
+
case 'tuple': {
|
|
447
445
|
if (args.length !== 1 || !ts.isArrayLiteralExpression(args[0])) {
|
|
448
|
-
throw new Error(
|
|
446
|
+
throw new Error('tuple() expects a single array literal argument');
|
|
449
447
|
}
|
|
450
448
|
const items = args[0].elements.map((element) => convertExpression(element, ctx).schema);
|
|
451
|
-
const schema = { type:
|
|
449
|
+
const schema = { type: 'array', minItems: items.length, maxItems: items.length };
|
|
452
450
|
if (items.length > 0) {
|
|
453
451
|
schema.prefixItems = items;
|
|
454
452
|
schema.items = false;
|
|
@@ -469,7 +467,7 @@ function convertCallExpression(call, ctx) {
|
|
|
469
467
|
const receiverExpr = unwrapExpression(callee.expression);
|
|
470
468
|
if (ts.isIdentifier(receiverExpr)) {
|
|
471
469
|
const base = receiverExpr.text;
|
|
472
|
-
if (base ===
|
|
470
|
+
if (base === 'z') {
|
|
473
471
|
return convertBaseZodCall(method, call.arguments, ctx);
|
|
474
472
|
}
|
|
475
473
|
}
|
|
@@ -477,8 +475,8 @@ function convertCallExpression(call, ctx) {
|
|
|
477
475
|
return applyMethod(receiver, method, call.arguments, ctx);
|
|
478
476
|
}
|
|
479
477
|
if (ts.isIdentifier(callee)) {
|
|
480
|
-
if (callee.text ===
|
|
481
|
-
throw new Error(
|
|
478
|
+
if (callee.text === 'z') {
|
|
479
|
+
throw new Error('Unexpected bare z() call');
|
|
482
480
|
}
|
|
483
481
|
}
|
|
484
482
|
throw new Error(`Unsupported call expression: ${call.getText()}`);
|
|
@@ -493,22 +491,22 @@ function convertExpression(node, ctx) {
|
|
|
493
491
|
// schema, so the intent is always the method call).
|
|
494
492
|
if (ts.isPropertyAccessExpression(expr)) {
|
|
495
493
|
const method = expr.name.text;
|
|
496
|
-
if (method ===
|
|
494
|
+
if (method === 'nullable' || method === 'optional') {
|
|
497
495
|
const receiver = convertExpression(expr.expression, ctx);
|
|
498
496
|
return applyMethod(receiver, method, ts.factory.createNodeArray([]), ctx);
|
|
499
497
|
}
|
|
500
498
|
}
|
|
501
|
-
if (ts.isIdentifier(expr) && expr.text ===
|
|
499
|
+
if (ts.isIdentifier(expr) && expr.text === 'undefined') {
|
|
502
500
|
return { schema: false, optional: true };
|
|
503
501
|
}
|
|
504
502
|
throw new Error(`Unsupported Zod expression: ${expr.getText()}`);
|
|
505
503
|
}
|
|
506
504
|
export function zodToJsonSchema(source) {
|
|
507
|
-
const sourceFile = ts.createSourceFile(
|
|
505
|
+
const sourceFile = ts.createSourceFile('schema.ts', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
508
506
|
const ctx = { warnings: [] };
|
|
509
507
|
const expressionStatement = sourceFile.statements.find(ts.isExpressionStatement);
|
|
510
508
|
if (!expressionStatement) {
|
|
511
|
-
throw new Error(
|
|
509
|
+
throw new Error('Expected a Zod expression');
|
|
512
510
|
}
|
|
513
511
|
const result = convertExpression(expressionStatement.expression, ctx);
|
|
514
512
|
if (result.schema !== false && isPlainObject(result.schema)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fetchAllChangeHistory, fetchEnvironments } from './launch/api.js';
|
|
2
|
-
import { InvalidSourceConfigError, getOutputPath, isLegacyLogLevel, slugify, transformConfig } from './launch/translate.js';
|
|
2
|
+
import { InvalidSourceConfigError, getOutputPath, isLegacyLogLevel, slugify, transformConfig, } from './launch/translate.js';
|
|
3
3
|
const SOURCE_NAME = 'launch';
|
|
4
4
|
const state = { apiKey: null, droppedOverrides: new Map(), envIdMap: null, skippedConfigs: [] };
|
|
5
5
|
class MissingAuthError extends Error {
|