@wp-typia/block-runtime 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -10
- package/dist/blocks.d.ts +180 -0
- package/dist/blocks.js +108 -0
- package/dist/defaults.d.ts +25 -1
- package/dist/defaults.js +73 -1
- package/dist/editor.d.ts +33 -1
- package/dist/editor.js +165 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/inspector-runtime.d.ts +129 -0
- package/dist/inspector-runtime.js +283 -0
- package/dist/inspector.d.ts +1 -1
- package/dist/inspector.js +1 -1
- package/dist/json-utils.d.ts +10 -0
- package/dist/json-utils.js +12 -0
- package/dist/metadata-analysis.d.ts +11 -0
- package/dist/metadata-analysis.js +285 -0
- package/dist/metadata-core.d.ts +286 -0
- package/dist/metadata-core.js +810 -0
- package/dist/metadata-model.d.ts +84 -0
- package/dist/metadata-model.js +59 -0
- package/dist/metadata-parser.d.ts +53 -0
- package/dist/metadata-parser.js +794 -0
- package/dist/metadata-php-render.d.ts +29 -0
- package/dist/metadata-php-render.js +549 -0
- package/dist/metadata-projection.d.ts +7 -0
- package/dist/metadata-projection.js +233 -0
- package/dist/migration-types.d.ts +53 -0
- package/dist/migration-types.js +1 -0
- package/dist/object-utils.d.ts +2 -0
- package/dist/object-utils.js +7 -0
- package/dist/schema-core.d.ts +267 -0
- package/dist/schema-core.js +597 -0
- package/dist/typia-tags.d.ts +11 -0
- package/dist/typia-tags.js +1 -0
- package/dist/validation.d.ts +53 -1
- package/dist/validation.js +206 -1
- package/package.json +16 -5
|
@@ -0,0 +1,810 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { getTaggedSyncBlockMetadataFailureCode } from './metadata-analysis.js';
|
|
4
|
+
import { renderPhpValidator } from './metadata-php-render.js';
|
|
5
|
+
import { analyzeSourceType, analyzeSourceTypes } from './metadata-parser.js';
|
|
6
|
+
import { createBlockJsonAttribute, createExampleValue, createManifestDocument, validateWordPressExtractionAttributes, } from './metadata-projection.js';
|
|
7
|
+
import { buildEndpointOpenApiDocument, manifestToJsonSchema, manifestToOpenApi, normalizeEndpointAuthDefinition, } from './schema-core.js';
|
|
8
|
+
/**
|
|
9
|
+
* Preserve literal TypeScript inference for backend-neutral endpoint manifests.
|
|
10
|
+
*
|
|
11
|
+
* @param manifest Canonical REST surface metadata authored in TypeScript.
|
|
12
|
+
* @returns The same manifest object with literal contract and endpoint metadata preserved.
|
|
13
|
+
*/
|
|
14
|
+
export function defineEndpointManifest(manifest) {
|
|
15
|
+
return manifest;
|
|
16
|
+
}
|
|
17
|
+
class GeneratedArtifactDriftError extends Error {
|
|
18
|
+
constructor(issues) {
|
|
19
|
+
const detail = issues
|
|
20
|
+
.map((issue) => `- ${issue.path} (${issue.reason})`)
|
|
21
|
+
.join('\n');
|
|
22
|
+
super(`Generated artifacts are missing or stale:\n${detail}`);
|
|
23
|
+
this.name = 'GeneratedArtifactDriftError';
|
|
24
|
+
this.issues = issues;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function reconcileGeneratedArtifacts(artifacts, executionOptions, preexistingIssues = []) {
|
|
28
|
+
if (executionOptions?.check !== true) {
|
|
29
|
+
for (const artifact of artifacts) {
|
|
30
|
+
fs.mkdirSync(path.dirname(artifact.path), { recursive: true });
|
|
31
|
+
fs.writeFileSync(artifact.path, artifact.content, 'utf8');
|
|
32
|
+
}
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const issues = [...preexistingIssues];
|
|
36
|
+
for (const artifact of artifacts) {
|
|
37
|
+
if (!fs.existsSync(artifact.path)) {
|
|
38
|
+
issues.push({
|
|
39
|
+
path: artifact.path,
|
|
40
|
+
reason: 'missing',
|
|
41
|
+
});
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const currentContent = fs.readFileSync(artifact.path, 'utf8');
|
|
45
|
+
if (normalizeGeneratedArtifactContentForComparison(currentContent) !==
|
|
46
|
+
normalizeGeneratedArtifactContentForComparison(artifact.content)) {
|
|
47
|
+
issues.push({
|
|
48
|
+
path: artifact.path,
|
|
49
|
+
reason: 'stale',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (issues.length > 0) {
|
|
54
|
+
throw new GeneratedArtifactDriftError(issues);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function normalizeGeneratedArtifactContentForComparison(content) {
|
|
58
|
+
return content.replace(/\r\n?/g, '\n');
|
|
59
|
+
}
|
|
60
|
+
function resolveSyncBlockMetadataPaths(options) {
|
|
61
|
+
const projectRoot = path.resolve(options.projectRoot ?? process.cwd());
|
|
62
|
+
const blockJsonPath = path.resolve(projectRoot, options.blockJsonFile);
|
|
63
|
+
const manifestRelativePath = options.manifestFile ??
|
|
64
|
+
path.join(path.dirname(options.blockJsonFile), 'typia.manifest.json');
|
|
65
|
+
const manifestPath = path.resolve(projectRoot, manifestRelativePath);
|
|
66
|
+
const phpValidatorPath = path.resolve(projectRoot, options.phpValidatorFile ??
|
|
67
|
+
path.join(path.dirname(manifestRelativePath), 'typia-validator.php'));
|
|
68
|
+
return {
|
|
69
|
+
blockJsonPath,
|
|
70
|
+
jsonSchemaPath: options.jsonSchemaFile
|
|
71
|
+
? path.resolve(projectRoot, options.jsonSchemaFile)
|
|
72
|
+
: null,
|
|
73
|
+
manifestPath,
|
|
74
|
+
openApiPath: options.openApiFile
|
|
75
|
+
? path.resolve(projectRoot, options.openApiFile)
|
|
76
|
+
: null,
|
|
77
|
+
phpValidatorPath,
|
|
78
|
+
projectRoot,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Synchronizes block metadata artifacts from a source TypeScript contract.
|
|
83
|
+
*
|
|
84
|
+
* This updates `block.json` attributes/examples and emits the related JSON
|
|
85
|
+
* Schema, manifest, OpenAPI, and optional PHP validator artifacts derived from
|
|
86
|
+
* the same source type.
|
|
87
|
+
*
|
|
88
|
+
* @param options Configuration for locating the project root, source types
|
|
89
|
+
* file/type name, and output artifact paths.
|
|
90
|
+
* @returns The generated artifact paths plus any lossy WordPress projection or
|
|
91
|
+
* PHP validator coverage warnings discovered during synchronization.
|
|
92
|
+
*/
|
|
93
|
+
export async function syncBlockMetadata(options, executionOptions = {}) {
|
|
94
|
+
const { blockJsonPath, jsonSchemaPath, manifestPath, openApiPath, phpValidatorPath } = resolveSyncBlockMetadataPaths(options);
|
|
95
|
+
const { rootNode } = analyzeSourceType(options);
|
|
96
|
+
if (rootNode.kind !== 'object' || rootNode.properties === undefined) {
|
|
97
|
+
throw new Error(`Source type "${options.sourceTypeName}" must resolve to an object shape`);
|
|
98
|
+
}
|
|
99
|
+
validateWordPressExtractionAttributes(rootNode.properties);
|
|
100
|
+
const driftIssues = [];
|
|
101
|
+
const lossyProjectionWarnings = [];
|
|
102
|
+
let blockJsonArtifact = null;
|
|
103
|
+
if (fs.existsSync(blockJsonPath)) {
|
|
104
|
+
const blockJson = JSON.parse(fs.readFileSync(blockJsonPath, 'utf8'));
|
|
105
|
+
blockJson.attributes = Object.fromEntries(Object.entries(rootNode.properties).map(([key, node]) => [
|
|
106
|
+
key,
|
|
107
|
+
createBlockJsonAttribute(node, lossyProjectionWarnings),
|
|
108
|
+
]));
|
|
109
|
+
blockJson.example = {
|
|
110
|
+
attributes: Object.fromEntries(Object.entries(rootNode.properties).map(([key, node]) => [
|
|
111
|
+
key,
|
|
112
|
+
createExampleValue(node, key),
|
|
113
|
+
])),
|
|
114
|
+
};
|
|
115
|
+
blockJsonArtifact = {
|
|
116
|
+
content: JSON.stringify(blockJson, null, '\t'),
|
|
117
|
+
path: blockJsonPath,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
else if (executionOptions.check === true) {
|
|
121
|
+
driftIssues.push({
|
|
122
|
+
path: blockJsonPath,
|
|
123
|
+
reason: 'missing',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
fs.readFileSync(blockJsonPath, 'utf8');
|
|
128
|
+
}
|
|
129
|
+
if (blockJsonArtifact === null) {
|
|
130
|
+
Object.values(rootNode.properties).forEach((node) => {
|
|
131
|
+
createBlockJsonAttribute(node, lossyProjectionWarnings);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
const manifest = createManifestDocument(options.sourceTypeName, rootNode.properties);
|
|
135
|
+
const manifestContent = JSON.stringify(manifest, null, '\t');
|
|
136
|
+
const jsonSchemaContent = jsonSchemaPath
|
|
137
|
+
? JSON.stringify(manifestToJsonSchema(manifest), null, '\t')
|
|
138
|
+
: null;
|
|
139
|
+
const openApiContent = openApiPath
|
|
140
|
+
? JSON.stringify(manifestToOpenApi(manifest, {
|
|
141
|
+
title: options.sourceTypeName,
|
|
142
|
+
}), null, '\t')
|
|
143
|
+
: null;
|
|
144
|
+
const phpValidator = renderPhpValidator(manifest);
|
|
145
|
+
reconcileGeneratedArtifacts([
|
|
146
|
+
...(blockJsonArtifact ? [blockJsonArtifact] : []),
|
|
147
|
+
{
|
|
148
|
+
content: manifestContent,
|
|
149
|
+
path: manifestPath,
|
|
150
|
+
},
|
|
151
|
+
...(jsonSchemaContent && jsonSchemaPath
|
|
152
|
+
? [
|
|
153
|
+
{
|
|
154
|
+
content: jsonSchemaContent,
|
|
155
|
+
path: jsonSchemaPath,
|
|
156
|
+
},
|
|
157
|
+
]
|
|
158
|
+
: []),
|
|
159
|
+
...(openApiContent && openApiPath
|
|
160
|
+
? [
|
|
161
|
+
{
|
|
162
|
+
content: openApiContent,
|
|
163
|
+
path: openApiPath,
|
|
164
|
+
},
|
|
165
|
+
]
|
|
166
|
+
: []),
|
|
167
|
+
{
|
|
168
|
+
content: phpValidator.source,
|
|
169
|
+
path: phpValidatorPath,
|
|
170
|
+
},
|
|
171
|
+
], executionOptions, driftIssues);
|
|
172
|
+
return {
|
|
173
|
+
attributeNames: Object.keys(rootNode.properties),
|
|
174
|
+
blockJsonPath,
|
|
175
|
+
...(jsonSchemaPath ? { jsonSchemaPath } : {}),
|
|
176
|
+
lossyProjectionWarnings: [...new Set(lossyProjectionWarnings)].sort(),
|
|
177
|
+
manifestPath,
|
|
178
|
+
...(openApiPath ? { openApiPath } : {}),
|
|
179
|
+
phpGenerationWarnings: [...new Set(phpValidator.warnings)].sort(),
|
|
180
|
+
phpValidatorPath,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Execute `syncBlockMetadata()` and return a structured status report.
|
|
185
|
+
*
|
|
186
|
+
* This wrapper preserves the existing artifact-generation behavior while adding
|
|
187
|
+
* stable status, warning, and failure metadata for scripts and CI integrations.
|
|
188
|
+
* Hard analysis failures are normalized into `failure`, and warning promotion is
|
|
189
|
+
* controlled by `strict`, `failOnLossy`, and `failOnPhpWarnings`.
|
|
190
|
+
*
|
|
191
|
+
* @param options Artifact generation inputs shared with `syncBlockMetadata()`.
|
|
192
|
+
* @param executionOptions Optional warning-promotion flags for CI/reporting flows.
|
|
193
|
+
* @returns A structured execution report describing generated paths, warnings, and failures.
|
|
194
|
+
*/
|
|
195
|
+
export async function runSyncBlockMetadata(options, executionOptions = {}) {
|
|
196
|
+
const strict = executionOptions.strict === true;
|
|
197
|
+
const failOnLossy = strict || executionOptions.failOnLossy === true;
|
|
198
|
+
const failOnPhpWarnings = strict || executionOptions.failOnPhpWarnings === true;
|
|
199
|
+
const resolvedPaths = resolveSyncBlockMetadataPaths(options);
|
|
200
|
+
try {
|
|
201
|
+
const result = await syncBlockMetadata(options, {
|
|
202
|
+
check: executionOptions.check,
|
|
203
|
+
});
|
|
204
|
+
const hasLossyWarnings = result.lossyProjectionWarnings.length > 0;
|
|
205
|
+
const hasPhpWarnings = result.phpGenerationWarnings.length > 0;
|
|
206
|
+
const hasWarnings = hasLossyWarnings || hasPhpWarnings;
|
|
207
|
+
const warningsAreErrors = (hasLossyWarnings && failOnLossy) || (hasPhpWarnings && failOnPhpWarnings);
|
|
208
|
+
return {
|
|
209
|
+
attributeNames: result.attributeNames,
|
|
210
|
+
blockJsonPath: result.blockJsonPath,
|
|
211
|
+
jsonSchemaPath: result.jsonSchemaPath ?? null,
|
|
212
|
+
lossyProjectionWarnings: result.lossyProjectionWarnings,
|
|
213
|
+
manifestPath: result.manifestPath,
|
|
214
|
+
openApiPath: result.openApiPath ?? null,
|
|
215
|
+
phpGenerationWarnings: result.phpGenerationWarnings,
|
|
216
|
+
phpValidatorPath: result.phpValidatorPath,
|
|
217
|
+
failure: null,
|
|
218
|
+
failOnLossy,
|
|
219
|
+
failOnPhpWarnings,
|
|
220
|
+
status: warningsAreErrors ? 'error' : hasWarnings ? 'warning' : 'success',
|
|
221
|
+
strict,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
return {
|
|
226
|
+
attributeNames: [],
|
|
227
|
+
blockJsonPath: resolvedPaths.blockJsonPath,
|
|
228
|
+
jsonSchemaPath: resolvedPaths.jsonSchemaPath,
|
|
229
|
+
lossyProjectionWarnings: [],
|
|
230
|
+
manifestPath: resolvedPaths.manifestPath,
|
|
231
|
+
openApiPath: resolvedPaths.openApiPath,
|
|
232
|
+
phpGenerationWarnings: [],
|
|
233
|
+
phpValidatorPath: resolvedPaths.phpValidatorPath,
|
|
234
|
+
failure: normalizeSyncBlockMetadataFailure(error),
|
|
235
|
+
failOnLossy,
|
|
236
|
+
failOnPhpWarnings,
|
|
237
|
+
status: 'error',
|
|
238
|
+
strict,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
export async function syncTypeSchemas(options, executionOptions = {}) {
|
|
243
|
+
const { projectRoot, rootNode } = analyzeSourceType(options);
|
|
244
|
+
if (rootNode.kind !== 'object' || rootNode.properties === undefined) {
|
|
245
|
+
throw new Error(`Source type "${options.sourceTypeName}" must resolve to an object shape for schema generation`);
|
|
246
|
+
}
|
|
247
|
+
const manifest = createManifestDocument(options.sourceTypeName, rootNode.properties);
|
|
248
|
+
const jsonSchemaPath = path.resolve(projectRoot, options.jsonSchemaFile);
|
|
249
|
+
const openApiPath = options.openApiFile
|
|
250
|
+
? path.resolve(projectRoot, options.openApiFile)
|
|
251
|
+
: undefined;
|
|
252
|
+
reconcileGeneratedArtifacts([
|
|
253
|
+
{
|
|
254
|
+
content: JSON.stringify(manifestToJsonSchema(manifest), null, '\t'),
|
|
255
|
+
path: jsonSchemaPath,
|
|
256
|
+
},
|
|
257
|
+
...(openApiPath
|
|
258
|
+
? [
|
|
259
|
+
{
|
|
260
|
+
content: JSON.stringify(manifestToOpenApi(manifest, options.openApiInfo ?? { title: options.sourceTypeName }), null, '\t'),
|
|
261
|
+
path: openApiPath,
|
|
262
|
+
},
|
|
263
|
+
]
|
|
264
|
+
: []),
|
|
265
|
+
], executionOptions);
|
|
266
|
+
return {
|
|
267
|
+
jsonSchemaPath,
|
|
268
|
+
openApiPath,
|
|
269
|
+
sourceTypeName: options.sourceTypeName,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Generate and write a canonical OpenAPI document for scaffolded REST contracts.
|
|
274
|
+
*
|
|
275
|
+
* @param options Contracts, endpoint metadata, source file, and output file settings.
|
|
276
|
+
* @returns Information about the generated OpenAPI document and included schema components.
|
|
277
|
+
*/
|
|
278
|
+
export async function syncRestOpenApi(options, executionOptions = {}) {
|
|
279
|
+
const { manifest, openApiPath, projectRoot, typesFile } = normalizeSyncRestOpenApiOptions(options);
|
|
280
|
+
const sourceTypeNames = Object.values(manifest.contracts).map((contract) => contract.sourceTypeName);
|
|
281
|
+
const analyzedTypes = analyzeSourceTypes({
|
|
282
|
+
projectRoot,
|
|
283
|
+
typesFile,
|
|
284
|
+
}, sourceTypeNames);
|
|
285
|
+
const contracts = Object.fromEntries(Object.entries(manifest.contracts).map(([contractKey, contract]) => {
|
|
286
|
+
const rootNode = analyzedTypes[contract.sourceTypeName];
|
|
287
|
+
if (rootNode.kind !== 'object' || rootNode.properties === undefined) {
|
|
288
|
+
throw new Error(`Source type "${contract.sourceTypeName}" must resolve to an object shape for REST OpenAPI generation`);
|
|
289
|
+
}
|
|
290
|
+
return [
|
|
291
|
+
contractKey,
|
|
292
|
+
{
|
|
293
|
+
document: createManifestDocument(contract.sourceTypeName, rootNode.properties),
|
|
294
|
+
...(typeof contract.schemaName === 'string' &&
|
|
295
|
+
contract.schemaName.length > 0
|
|
296
|
+
? { schemaName: contract.schemaName }
|
|
297
|
+
: {}),
|
|
298
|
+
},
|
|
299
|
+
];
|
|
300
|
+
}));
|
|
301
|
+
reconcileGeneratedArtifacts([
|
|
302
|
+
{
|
|
303
|
+
content: JSON.stringify(buildEndpointOpenApiDocument({
|
|
304
|
+
contracts,
|
|
305
|
+
endpoints: manifest.endpoints,
|
|
306
|
+
info: manifest.info,
|
|
307
|
+
}), null, '\t'),
|
|
308
|
+
path: openApiPath,
|
|
309
|
+
},
|
|
310
|
+
], executionOptions);
|
|
311
|
+
return {
|
|
312
|
+
endpointCount: manifest.endpoints.length,
|
|
313
|
+
openApiPath,
|
|
314
|
+
schemaNames: Object.values(manifest.contracts).map((contract) => contract.schemaName ?? contract.sourceTypeName),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Generate and write a manifest-first portable endpoint client module.
|
|
319
|
+
*
|
|
320
|
+
* @param options Manifest, source file, validator file, and output path settings.
|
|
321
|
+
* @returns Information about the generated client file and emitted operation ids.
|
|
322
|
+
*/
|
|
323
|
+
export async function syncEndpointClient(options, executionOptions = {}) {
|
|
324
|
+
const { clientPath, manifest, projectRoot, typesFile, validatorsFile } = normalizeSyncEndpointClientOptions(options);
|
|
325
|
+
analyzeSourceTypes({ projectRoot, typesFile }, [...new Set(Object.values(manifest.contracts).map((contract) => contract.sourceTypeName))]);
|
|
326
|
+
const operationIds = new Set();
|
|
327
|
+
const importedTypeNames = new Set();
|
|
328
|
+
const endpointLines = [];
|
|
329
|
+
const inlineHelpers = new Set();
|
|
330
|
+
const validatorPropertyNames = new Map();
|
|
331
|
+
const hasCombinedRequestEndpoints = manifest.endpoints.some((endpoint) => Boolean(endpoint.bodyContract && endpoint.queryContract));
|
|
332
|
+
const occupiedIdentifiers = new Set([
|
|
333
|
+
'apiValidators',
|
|
334
|
+
'callEndpoint',
|
|
335
|
+
'createEndpoint',
|
|
336
|
+
...(manifest.endpoints.some((endpoint) => !endpoint.bodyContract && !endpoint.queryContract)
|
|
337
|
+
? ['validateNoRequest']
|
|
338
|
+
: []),
|
|
339
|
+
...(hasCombinedRequestEndpoints ? ['validateCombinedRequest'] : []),
|
|
340
|
+
]);
|
|
341
|
+
for (const endpoint of manifest.endpoints) {
|
|
342
|
+
const normalizedAuth = normalizeEndpointAuthDefinition(endpoint);
|
|
343
|
+
const endpointConstantName = `${endpoint.operationId}Endpoint`;
|
|
344
|
+
assertValidClientIdentifier(endpoint.operationId, 'operationId');
|
|
345
|
+
assertValidClientIdentifier(endpointConstantName, 'endpoint constant');
|
|
346
|
+
if (operationIds.has(endpoint.operationId)) {
|
|
347
|
+
throw new Error(`Duplicate endpoint operationId "${endpoint.operationId}" detected while generating the endpoint client.`);
|
|
348
|
+
}
|
|
349
|
+
for (const identifier of [endpoint.operationId, endpointConstantName]) {
|
|
350
|
+
if (occupiedIdentifiers.has(identifier)) {
|
|
351
|
+
throw new Error(`Generated endpoint client identifier "${identifier}" collides with another emitted symbol.`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
operationIds.add(endpoint.operationId);
|
|
355
|
+
occupiedIdentifiers.add(endpoint.operationId);
|
|
356
|
+
occupiedIdentifiers.add(endpointConstantName);
|
|
357
|
+
const queryContractKey = endpoint.queryContract ?? null;
|
|
358
|
+
const bodyContractKey = endpoint.bodyContract ?? null;
|
|
359
|
+
const hasRequest = Boolean(queryContractKey || bodyContractKey);
|
|
360
|
+
const responseContract = resolveEndpointClientContract(manifest, endpoint.responseContract, endpoint.operationId, 'responseContract');
|
|
361
|
+
importedTypeNames.add(responseContract.sourceTypeName);
|
|
362
|
+
let requestTypeName = 'undefined';
|
|
363
|
+
let requestValidatorExpression = 'validateNoRequest';
|
|
364
|
+
let requestLocationExpression = null;
|
|
365
|
+
const queryContract = queryContractKey
|
|
366
|
+
? resolveEndpointClientContract(manifest, queryContractKey, endpoint.operationId, 'queryContract')
|
|
367
|
+
: null;
|
|
368
|
+
const bodyContract = bodyContractKey
|
|
369
|
+
? resolveEndpointClientContract(manifest, bodyContractKey, endpoint.operationId, 'bodyContract')
|
|
370
|
+
: null;
|
|
371
|
+
if (queryContract && bodyContract) {
|
|
372
|
+
const queryValidatorExpression = toValidatorAccessExpression(queryContractKey, validatorPropertyNames);
|
|
373
|
+
const bodyValidatorExpression = toValidatorAccessExpression(bodyContractKey, validatorPropertyNames);
|
|
374
|
+
requestTypeName = `{ query: ${queryContract.sourceTypeName}; body: ${bodyContract.sourceTypeName} }`;
|
|
375
|
+
requestValidatorExpression = `(input) => validateCombinedRequest( input, ${queryValidatorExpression}, ${bodyValidatorExpression} )`;
|
|
376
|
+
requestLocationExpression = "'query-and-body'";
|
|
377
|
+
importedTypeNames.add(queryContract.sourceTypeName);
|
|
378
|
+
importedTypeNames.add(bodyContract.sourceTypeName);
|
|
379
|
+
inlineHelpers.add('validateCombinedRequest');
|
|
380
|
+
}
|
|
381
|
+
else if (queryContract) {
|
|
382
|
+
requestTypeName = queryContract.sourceTypeName;
|
|
383
|
+
requestValidatorExpression = toValidatorAccessExpression(queryContractKey, validatorPropertyNames);
|
|
384
|
+
requestLocationExpression = "'query'";
|
|
385
|
+
importedTypeNames.add(queryContract.sourceTypeName);
|
|
386
|
+
}
|
|
387
|
+
else if (bodyContract) {
|
|
388
|
+
requestTypeName = bodyContract.sourceTypeName;
|
|
389
|
+
requestValidatorExpression = toValidatorAccessExpression(bodyContractKey, validatorPropertyNames);
|
|
390
|
+
requestLocationExpression = "'body'";
|
|
391
|
+
importedTypeNames.add(bodyContract.sourceTypeName);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
inlineHelpers.add('validateNoRequest');
|
|
395
|
+
}
|
|
396
|
+
const returnCallExpression = hasRequest
|
|
397
|
+
? `callEndpoint( ${endpoint.operationId}Endpoint, request, options )`
|
|
398
|
+
: `callEndpoint( ${endpoint.operationId}Endpoint, undefined, options )`;
|
|
399
|
+
const returnCallLines = returnCallExpression.length <= 68
|
|
400
|
+
? [`\treturn ${returnCallExpression};`]
|
|
401
|
+
: [
|
|
402
|
+
`\treturn callEndpoint(`,
|
|
403
|
+
`\t\t${endpoint.operationId}Endpoint,`,
|
|
404
|
+
`\t\t${hasRequest ? 'request' : 'undefined'},`,
|
|
405
|
+
`\t\toptions`,
|
|
406
|
+
`\t);`,
|
|
407
|
+
];
|
|
408
|
+
endpointLines.push([
|
|
409
|
+
`export const ${endpointConstantName} = createEndpoint<`,
|
|
410
|
+
`\t${requestTypeName},`,
|
|
411
|
+
`\t${responseContract.sourceTypeName}`,
|
|
412
|
+
`>( {`,
|
|
413
|
+
`\tauthIntent: ${toJavaScriptStringLiteral(normalizedAuth.auth)},`,
|
|
414
|
+
...(normalizedAuth.authMode
|
|
415
|
+
? [`\tauthMode: ${toJavaScriptStringLiteral(normalizedAuth.authMode)},`]
|
|
416
|
+
: []),
|
|
417
|
+
`\tmethod: ${toJavaScriptStringLiteral(endpoint.method)},`,
|
|
418
|
+
`\toperationId: ${toJavaScriptStringLiteral(endpoint.operationId)},`,
|
|
419
|
+
`\tpath: ${toJavaScriptStringLiteral(endpoint.path)},`,
|
|
420
|
+
...(requestLocationExpression
|
|
421
|
+
? [`\trequestLocation: ${requestLocationExpression},`]
|
|
422
|
+
: []),
|
|
423
|
+
`\tvalidateRequest: ${requestValidatorExpression},`,
|
|
424
|
+
`\tvalidateResponse: ${toValidatorAccessExpression(endpoint.responseContract, validatorPropertyNames)},`,
|
|
425
|
+
`} );`,
|
|
426
|
+
'',
|
|
427
|
+
`export function ${endpoint.operationId}(`,
|
|
428
|
+
...(hasRequest ? [`\trequest: ${requestTypeName},`] : []),
|
|
429
|
+
`\toptions: EndpointCallOptions`,
|
|
430
|
+
`) {`,
|
|
431
|
+
...returnCallLines,
|
|
432
|
+
`}`,
|
|
433
|
+
].join('\n'));
|
|
434
|
+
}
|
|
435
|
+
const sortedTypeNames = [...importedTypeNames].sort();
|
|
436
|
+
const helperTypeNames = new Set(sortedTypeNames);
|
|
437
|
+
const combinedValidationErrorTypeName = inlineHelpers.has('validateCombinedRequest')
|
|
438
|
+
? reserveUniqueClientTypeIdentifier('PortableValidationError', helperTypeNames)
|
|
439
|
+
: null;
|
|
440
|
+
const combinedValidationResultTypeName = inlineHelpers.has('validateCombinedRequest')
|
|
441
|
+
? reserveUniqueClientTypeIdentifier('PortableValidationResult', helperTypeNames)
|
|
442
|
+
: null;
|
|
443
|
+
const lines = [
|
|
444
|
+
`import {`,
|
|
445
|
+
`\tcallEndpoint,`,
|
|
446
|
+
`\tcreateEndpoint,`,
|
|
447
|
+
`\ttype EndpointCallOptions,`,
|
|
448
|
+
...(inlineHelpers.has('validateCombinedRequest')
|
|
449
|
+
? [
|
|
450
|
+
`\ttype ValidationError as ${combinedValidationErrorTypeName},`,
|
|
451
|
+
`\ttype ValidationResult as ${combinedValidationResultTypeName},`,
|
|
452
|
+
]
|
|
453
|
+
: []),
|
|
454
|
+
`} from '@wp-typia/api-client';`,
|
|
455
|
+
...(sortedTypeNames.length === 1
|
|
456
|
+
? [
|
|
457
|
+
`import type { ${sortedTypeNames[0]} } from ${toJavaScriptStringLiteral(toModuleImportPath(clientPath, path.resolve(projectRoot, typesFile)))};`,
|
|
458
|
+
]
|
|
459
|
+
: [
|
|
460
|
+
`import type {`,
|
|
461
|
+
...sortedTypeNames.map((typeName) => `\t${typeName},`),
|
|
462
|
+
`} from ${toJavaScriptStringLiteral(toModuleImportPath(clientPath, path.resolve(projectRoot, typesFile)))};`,
|
|
463
|
+
]),
|
|
464
|
+
`import { apiValidators } from ${toJavaScriptStringLiteral(toModuleImportPath(clientPath, path.resolve(projectRoot, validatorsFile)))};`,
|
|
465
|
+
'',
|
|
466
|
+
...(inlineHelpers.has('validateNoRequest')
|
|
467
|
+
? [
|
|
468
|
+
`function validateNoRequest(input: unknown) {`,
|
|
469
|
+
`\tif (input !== undefined) {`,
|
|
470
|
+
`\t\treturn {`,
|
|
471
|
+
`\t\t\tdata: undefined,`,
|
|
472
|
+
`\t\t\terrors: [`,
|
|
473
|
+
`\t\t\t\t{`,
|
|
474
|
+
`\t\t\t\t\texpected: 'undefined',`,
|
|
475
|
+
`\t\t\t\t\tpath: '(root)',`,
|
|
476
|
+
`\t\t\t\t\tvalue: input,`,
|
|
477
|
+
`\t\t\t\t},`,
|
|
478
|
+
`\t\t\t],`,
|
|
479
|
+
`\t\t\tisValid: false,`,
|
|
480
|
+
`\t\t};`,
|
|
481
|
+
`\t}`,
|
|
482
|
+
'',
|
|
483
|
+
`\treturn {`,
|
|
484
|
+
`\t\tdata: undefined,`,
|
|
485
|
+
`\t\terrors: [],`,
|
|
486
|
+
`\t\tisValid: true,`,
|
|
487
|
+
`\t};`,
|
|
488
|
+
`}`,
|
|
489
|
+
'',
|
|
490
|
+
]
|
|
491
|
+
: []),
|
|
492
|
+
...(inlineHelpers.has('validateCombinedRequest')
|
|
493
|
+
? [
|
|
494
|
+
`function validateCombinedRequest<TQuery, TBody>(`,
|
|
495
|
+
`\tinput: unknown,`,
|
|
496
|
+
`\tvalidateQuery: (input: unknown) => ${combinedValidationResultTypeName}<TQuery>,`,
|
|
497
|
+
`\tvalidateBody: (input: unknown) => ${combinedValidationResultTypeName}<TBody>,`,
|
|
498
|
+
`): ${combinedValidationResultTypeName}<{ query: TQuery; body: TBody }> {`,
|
|
499
|
+
`\tif ( input === null || typeof input !== 'object' || Array.isArray( input ) ) {`,
|
|
500
|
+
`\t\treturn {`,
|
|
501
|
+
`\t\t\tdata: undefined,`,
|
|
502
|
+
`\t\t\terrors: [`,
|
|
503
|
+
`\t\t\t\t{`,
|
|
504
|
+
`\t\t\t\t\texpected: '{ query, body }',`,
|
|
505
|
+
`\t\t\t\t\tpath: '(root)',`,
|
|
506
|
+
`\t\t\t\t\tvalue: input,`,
|
|
507
|
+
`\t\t\t\t},`,
|
|
508
|
+
`\t\t\t],`,
|
|
509
|
+
`\t\t\tisValid: false,`,
|
|
510
|
+
`\t\t};`,
|
|
511
|
+
`\t}`,
|
|
512
|
+
``,
|
|
513
|
+
`\tconst request = input as { query?: unknown; body?: unknown };`,
|
|
514
|
+
`\tif ( !Object.prototype.hasOwnProperty.call( request, 'query' ) || !Object.prototype.hasOwnProperty.call( request, 'body' ) ) {`,
|
|
515
|
+
`\t\treturn {`,
|
|
516
|
+
`\t\t\tdata: undefined,`,
|
|
517
|
+
`\t\t\terrors: [`,
|
|
518
|
+
`\t\t\t\t{`,
|
|
519
|
+
`\t\t\t\t\texpected: '{ query, body }',`,
|
|
520
|
+
`\t\t\t\t\tpath: '(root)',`,
|
|
521
|
+
`\t\t\t\t\tvalue: input,`,
|
|
522
|
+
`\t\t\t\t},`,
|
|
523
|
+
`\t\t\t],`,
|
|
524
|
+
`\t\t\tisValid: false,`,
|
|
525
|
+
`\t\t};`,
|
|
526
|
+
`\t}`,
|
|
527
|
+
``,
|
|
528
|
+
`\tconst prefixPath = (prefix: '$.query' | '$.body', path: string): string => {`,
|
|
529
|
+
`\t\tif ( path === '(root)' ) {`,
|
|
530
|
+
`\t\t\treturn prefix;`,
|
|
531
|
+
`\t\t}`,
|
|
532
|
+
``,
|
|
533
|
+
`\t\treturn path.startsWith( '$' ) ? \`\${prefix}\${path.slice( 1 )}\` : \`\${prefix}.\${path}\`;`,
|
|
534
|
+
`\t};`,
|
|
535
|
+
``,
|
|
536
|
+
`\tconst queryValidation = validateQuery( request.query );`,
|
|
537
|
+
`\tconst bodyValidation = validateBody( request.body );`,
|
|
538
|
+
`\tconst errors: ${combinedValidationErrorTypeName}[] = [`,
|
|
539
|
+
`\t\t...queryValidation.errors.map( ( error ) => ( {`,
|
|
540
|
+
`\t\t\t...error,`,
|
|
541
|
+
`\t\t\tpath: prefixPath( '$.query', error.path ),`,
|
|
542
|
+
`\t\t} ) ),`,
|
|
543
|
+
`\t\t...bodyValidation.errors.map( ( error ) => ( {`,
|
|
544
|
+
`\t\t\t...error,`,
|
|
545
|
+
`\t\t\tpath: prefixPath( '$.body', error.path ),`,
|
|
546
|
+
`\t\t} ) ),`,
|
|
547
|
+
`\t];`,
|
|
548
|
+
``,
|
|
549
|
+
`\tif ( !queryValidation.isValid || !bodyValidation.isValid ) {`,
|
|
550
|
+
`\t\treturn {`,
|
|
551
|
+
`\t\t\tdata: undefined,`,
|
|
552
|
+
`\t\t\terrors,`,
|
|
553
|
+
`\t\t\tisValid: false,`,
|
|
554
|
+
`\t\t};`,
|
|
555
|
+
`\t}`,
|
|
556
|
+
``,
|
|
557
|
+
`\treturn {`,
|
|
558
|
+
`\t\tdata: {`,
|
|
559
|
+
`\t\t\tbody: bodyValidation.data ?? ( request.body as TBody ),`,
|
|
560
|
+
`\t\t\tquery: queryValidation.data ?? ( request.query as TQuery ),`,
|
|
561
|
+
`\t\t},`,
|
|
562
|
+
`\t\terrors: [],`,
|
|
563
|
+
`\t\tisValid: true,`,
|
|
564
|
+
`\t};`,
|
|
565
|
+
`}`,
|
|
566
|
+
'',
|
|
567
|
+
]
|
|
568
|
+
: []),
|
|
569
|
+
...endpointLines.flatMap((entry) => [entry, '']),
|
|
570
|
+
];
|
|
571
|
+
reconcileGeneratedArtifacts([
|
|
572
|
+
{
|
|
573
|
+
content: `${lines.join('\n').trimEnd()}\n`,
|
|
574
|
+
path: clientPath,
|
|
575
|
+
},
|
|
576
|
+
], executionOptions);
|
|
577
|
+
return {
|
|
578
|
+
clientPath,
|
|
579
|
+
endpointCount: manifest.endpoints.length,
|
|
580
|
+
operationIds: [...operationIds],
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function normalizeSyncRestOpenApiOptions(options) {
|
|
584
|
+
const projectRoot = path.resolve(options.projectRoot ?? process.cwd());
|
|
585
|
+
const openApiPath = path.resolve(projectRoot, options.openApiFile);
|
|
586
|
+
if ('manifest' in options) {
|
|
587
|
+
const hasDecomposedInputs = 'contracts' in options ||
|
|
588
|
+
'endpoints' in options ||
|
|
589
|
+
'openApiInfo' in options;
|
|
590
|
+
if (hasDecomposedInputs) {
|
|
591
|
+
throw new Error('syncRestOpenApi() accepts either { manifest, ... } or { contracts, endpoints, ... }, but not both.');
|
|
592
|
+
}
|
|
593
|
+
if (options.manifest == null) {
|
|
594
|
+
throw new Error('syncRestOpenApi() requires a manifest object when using { manifest, ... }.');
|
|
595
|
+
}
|
|
596
|
+
return {
|
|
597
|
+
manifest: options.manifest,
|
|
598
|
+
openApiPath,
|
|
599
|
+
projectRoot,
|
|
600
|
+
typesFile: options.typesFile,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
return {
|
|
604
|
+
manifest: {
|
|
605
|
+
contracts: options.contracts,
|
|
606
|
+
endpoints: options.endpoints,
|
|
607
|
+
...(options.openApiInfo ? { info: options.openApiInfo } : {}),
|
|
608
|
+
},
|
|
609
|
+
openApiPath,
|
|
610
|
+
projectRoot,
|
|
611
|
+
typesFile: options.typesFile,
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
function normalizeSyncEndpointClientOptions(options) {
|
|
615
|
+
const projectRoot = path.resolve(options.projectRoot ?? process.cwd());
|
|
616
|
+
const clientPath = path.resolve(projectRoot, options.clientFile);
|
|
617
|
+
const typesFile = path.resolve(projectRoot, options.typesFile);
|
|
618
|
+
const inferredValidatorsFile = options.validatorsFile ??
|
|
619
|
+
(() => {
|
|
620
|
+
const nextPath = options.typesFile.replace(/api-types\.ts$/u, 'api-validators.ts');
|
|
621
|
+
if (nextPath === options.typesFile) {
|
|
622
|
+
throw new Error('syncEndpointClient() could not infer validatorsFile from typesFile; pass validatorsFile explicitly.');
|
|
623
|
+
}
|
|
624
|
+
return nextPath;
|
|
625
|
+
})();
|
|
626
|
+
const validatorsFile = path.resolve(projectRoot, inferredValidatorsFile);
|
|
627
|
+
if (!fs.existsSync(typesFile)) {
|
|
628
|
+
throw new Error(`Unable to generate an endpoint client because the types file does not exist: ${typesFile}`);
|
|
629
|
+
}
|
|
630
|
+
if (!fs.existsSync(validatorsFile)) {
|
|
631
|
+
throw new Error(`Unable to generate an endpoint client because the validators file does not exist: ${validatorsFile}`);
|
|
632
|
+
}
|
|
633
|
+
return {
|
|
634
|
+
clientPath,
|
|
635
|
+
manifest: options.manifest,
|
|
636
|
+
projectRoot,
|
|
637
|
+
typesFile: options.typesFile,
|
|
638
|
+
validatorsFile: path.relative(projectRoot, validatorsFile),
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function resolveEndpointClientContract(manifest, contractKey, operationId, fieldName) {
|
|
642
|
+
const contract = manifest.contracts[contractKey];
|
|
643
|
+
if (!contract) {
|
|
644
|
+
throw new Error(`Endpoint "${operationId}" references missing ${fieldName} "${contractKey}" while generating the endpoint client.`);
|
|
645
|
+
}
|
|
646
|
+
return contract;
|
|
647
|
+
}
|
|
648
|
+
function toClientPropertyName(value) {
|
|
649
|
+
return value
|
|
650
|
+
.replace(/[^A-Za-z0-9]+(.)/g, (_match, next) => next.toUpperCase())
|
|
651
|
+
.replace(/^[A-Z]/, (match) => match.toLowerCase());
|
|
652
|
+
}
|
|
653
|
+
function toValidatorAccessExpression(contractKey, seenPropertyNames) {
|
|
654
|
+
const propertyName = toClientPropertyName(contractKey);
|
|
655
|
+
const previousContractKey = seenPropertyNames.get(propertyName);
|
|
656
|
+
if (previousContractKey && previousContractKey !== contractKey) {
|
|
657
|
+
throw new Error(`Contract keys "${previousContractKey}" and "${contractKey}" both normalize to apiValidators[${toJavaScriptStringLiteral(propertyName)}] while generating the endpoint client.`);
|
|
658
|
+
}
|
|
659
|
+
seenPropertyNames.set(propertyName, contractKey);
|
|
660
|
+
return /^[$A-Z_][0-9A-Z_$]*$/iu.test(propertyName)
|
|
661
|
+
? `apiValidators.${propertyName}`
|
|
662
|
+
: `apiValidators[${toJavaScriptStringLiteral(propertyName)}]`;
|
|
663
|
+
}
|
|
664
|
+
function toModuleImportPath(fromFile, targetFile) {
|
|
665
|
+
let relativePath = path.relative(path.dirname(fromFile), targetFile).replace(/\\/g, '/');
|
|
666
|
+
if (!relativePath.startsWith('.')) {
|
|
667
|
+
relativePath = `./${relativePath}`;
|
|
668
|
+
}
|
|
669
|
+
return relativePath.replace(/\.[^.]+$/u, '');
|
|
670
|
+
}
|
|
671
|
+
function toJavaScriptStringLiteral(value) {
|
|
672
|
+
return `'${value
|
|
673
|
+
.replace(/\\/g, '\\\\')
|
|
674
|
+
.replace(/'/g, "\\'")
|
|
675
|
+
.replace(/\r/g, '\\r')
|
|
676
|
+
.replace(/\n/g, '\\n')}'`;
|
|
677
|
+
}
|
|
678
|
+
const RESERVED_CLIENT_IDENTIFIERS = new Set([
|
|
679
|
+
'await',
|
|
680
|
+
'break',
|
|
681
|
+
'case',
|
|
682
|
+
'catch',
|
|
683
|
+
'class',
|
|
684
|
+
'const',
|
|
685
|
+
'continue',
|
|
686
|
+
'debugger',
|
|
687
|
+
'default',
|
|
688
|
+
'delete',
|
|
689
|
+
'do',
|
|
690
|
+
'else',
|
|
691
|
+
'enum',
|
|
692
|
+
'export',
|
|
693
|
+
'extends',
|
|
694
|
+
'false',
|
|
695
|
+
'finally',
|
|
696
|
+
'for',
|
|
697
|
+
'function',
|
|
698
|
+
'if',
|
|
699
|
+
'implements',
|
|
700
|
+
'import',
|
|
701
|
+
'in',
|
|
702
|
+
'interface',
|
|
703
|
+
'instanceof',
|
|
704
|
+
'let',
|
|
705
|
+
'new',
|
|
706
|
+
'null',
|
|
707
|
+
'package',
|
|
708
|
+
'private',
|
|
709
|
+
'protected',
|
|
710
|
+
'public',
|
|
711
|
+
'return',
|
|
712
|
+
'static',
|
|
713
|
+
'super',
|
|
714
|
+
'switch',
|
|
715
|
+
'this',
|
|
716
|
+
'throw',
|
|
717
|
+
'true',
|
|
718
|
+
'try',
|
|
719
|
+
'typeof',
|
|
720
|
+
'var',
|
|
721
|
+
'void',
|
|
722
|
+
'while',
|
|
723
|
+
'with',
|
|
724
|
+
'yield',
|
|
725
|
+
]);
|
|
726
|
+
/**
|
|
727
|
+
* Guard generated client identifiers so emitted modules remain valid JavaScript.
|
|
728
|
+
*
|
|
729
|
+
* @param value Candidate identifier to validate before code generation.
|
|
730
|
+
* @param label Human-readable label for error reporting.
|
|
731
|
+
* @throws {Error} When the identifier is syntactically invalid or reserved.
|
|
732
|
+
*/
|
|
733
|
+
function assertValidClientIdentifier(value, label) {
|
|
734
|
+
if (!/^[$A-Z_][0-9A-Z_$]*$/iu.test(value)) {
|
|
735
|
+
throw new Error(`Generated endpoint client ${label} "${value}" is not a valid JavaScript identifier.`);
|
|
736
|
+
}
|
|
737
|
+
if (RESERVED_CLIENT_IDENTIFIERS.has(value)) {
|
|
738
|
+
throw new Error(`Generated endpoint client ${label} "${value}" is a reserved JavaScript identifier.`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
function reserveUniqueClientTypeIdentifier(preferred, occupied) {
|
|
742
|
+
let suffix = 0;
|
|
743
|
+
while (true) {
|
|
744
|
+
const candidate = suffix === 0
|
|
745
|
+
? preferred
|
|
746
|
+
: suffix === 1
|
|
747
|
+
? `${preferred}Alias`
|
|
748
|
+
: `${preferred}Alias${suffix}`;
|
|
749
|
+
if (!occupied.has(candidate) && !RESERVED_CLIENT_IDENTIFIERS.has(candidate)) {
|
|
750
|
+
assertValidClientIdentifier(candidate, 'type alias');
|
|
751
|
+
occupied.add(candidate);
|
|
752
|
+
return candidate;
|
|
753
|
+
}
|
|
754
|
+
suffix += 1;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
function normalizeSyncBlockMetadataFailure(error) {
|
|
758
|
+
if (error instanceof Error) {
|
|
759
|
+
return {
|
|
760
|
+
code: resolveSyncBlockMetadataFailureCode(error),
|
|
761
|
+
message: error.message,
|
|
762
|
+
name: error.name,
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
return {
|
|
766
|
+
code: 'unknown-internal-error',
|
|
767
|
+
message: String(error),
|
|
768
|
+
name: 'NonErrorThrow',
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
function resolveSyncBlockMetadataFailureCode(error) {
|
|
772
|
+
if (error instanceof GeneratedArtifactDriftError) {
|
|
773
|
+
return 'stale-generated-artifact';
|
|
774
|
+
}
|
|
775
|
+
const taggedCode = getTaggedSyncBlockMetadataFailureCode(error);
|
|
776
|
+
if (taggedCode) {
|
|
777
|
+
return taggedCode;
|
|
778
|
+
}
|
|
779
|
+
const { message } = error;
|
|
780
|
+
if (message.startsWith('Unsupported type node at ')) {
|
|
781
|
+
return 'unsupported-type-node';
|
|
782
|
+
}
|
|
783
|
+
if (message.startsWith('Recursive types are not supported:') ||
|
|
784
|
+
message.startsWith('Recursive type')) {
|
|
785
|
+
return 'recursive-type';
|
|
786
|
+
}
|
|
787
|
+
if (message.startsWith('Unable to load types file:') ||
|
|
788
|
+
message.startsWith('Unable to find source type "') ||
|
|
789
|
+
message.startsWith('Unable to resolve type reference "') ||
|
|
790
|
+
message.includes('must resolve to an object shape')) {
|
|
791
|
+
return 'invalid-source-type';
|
|
792
|
+
}
|
|
793
|
+
if (message.startsWith('Unsupported ') ||
|
|
794
|
+
message.startsWith('Mixed primitive enums are not supported at ') ||
|
|
795
|
+
message.startsWith('Indexed access ') ||
|
|
796
|
+
message.startsWith('Intersection at ') ||
|
|
797
|
+
message.startsWith('WordPress extraction ') ||
|
|
798
|
+
message.startsWith('Generic type declarations are not supported at ') ||
|
|
799
|
+
message.startsWith('Generic type references are not supported at ') ||
|
|
800
|
+
message.startsWith('Class and enum references are not supported at ') ||
|
|
801
|
+
message.startsWith('Discriminated union at ') ||
|
|
802
|
+
message.startsWith('External or non-serializable ') ||
|
|
803
|
+
message.startsWith('Conflicting ') ||
|
|
804
|
+
message.startsWith('Tag "') ||
|
|
805
|
+
message.startsWith('Only object-like interface extensions are supported:') ||
|
|
806
|
+
message.startsWith('Array type is missing an item type at ')) {
|
|
807
|
+
return 'unsupported-type-pattern';
|
|
808
|
+
}
|
|
809
|
+
return 'unknown-internal-error';
|
|
810
|
+
}
|