push neon reactor

This commit is contained in:
2026-07-12 15:11:38 +02:00
parent 172e72dbcd
commit aeab5f7820
9597 changed files with 2407488 additions and 0 deletions
@@ -0,0 +1,29 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
export interface ApiResponseInfo {
type?: string;
format?: string;
}
export interface ApiParameter {
name: string;
in: string;
required: boolean;
type: string;
format?: string;
}
export interface ApiDefinition {
path: string;
method: string;
parameters?: ApiParameter[];
responseInfo?: Record<string, ApiResponseInfo>;
}
export interface DataSourceInfo {
tableId: string;
version?: string;
primaryKey?: string;
dataSourceType?: string;
apis: Record<string, ApiDefinition>;
}
export type DataSourcesInfo = Record<string, DataSourceInfo>;
//# sourceMappingURL=dataSourceInfo.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"dataSourceInfo.d.ts","sourceRoot":"","sources":["../../src/CodeGen/dataSourceInfo.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAChD;AAMD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACrC;AAKD,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC"}
@@ -0,0 +1,5 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
export {};
//# sourceMappingURL=dataSourceInfo.js.map
@@ -0,0 +1 @@
{"version":3,"file":"dataSourceInfo.js","sourceRoot":"","sources":["../../src/CodeGen/dataSourceInfo.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,10 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import type { ILogger } from '../services/index.js';
/**
* Processes all JSON files in the specified folder and generates a TypeScript file with data source information.
* @param schemaFolderPath The path to the folder containing JSON files.
*/
export declare function processDataSourceInfo(schemaFolderPath: string, logger: ILogger): Promise<void>;
//# sourceMappingURL=dataSourceInfoProcessor.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"dataSourceInfoProcessor.d.ts","sourceRoot":"","sources":["../../src/CodeGen/dataSourceInfoProcessor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAiB3C;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,IAAI,CAAC,CAmDf"}
@@ -0,0 +1,555 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import { getVfs } from '../VfsManager.js';
import { isDataverseEntity } from './shared/dataverseUtils.js';
import { extractModelName } from './shared/modelUtils.js';
import { getAllJsonFiles, isJsonObject } from './shared/schemaUtils.js';
import { getSqlStoredProcApiKey } from './shared/sqlUtils.js';
/** Regex to match file extension (e.g., ".json", ".ts") */
const FILE_EXTENSION_PATTERN = /\.[^/.]+$/;
/**
* Processes all JSON files in the specified folder and generates a TypeScript file with data source information.
* @param schemaFolderPath The path to the folder containing JSON files.
*/
export async function processDataSourceInfo(schemaFolderPath, logger) {
const scenario = logger.trackScenario('ProcessDataSourceInfo', { schemaFolderPath });
const vfs = getVfs();
try {
const jsonFiles = await getAllJsonFiles(schemaFolderPath);
const resultJson = {};
for (const filePath of jsonFiles) {
try {
const jsonContent = await vfs.readFile(filePath, 'utf-8');
const jsonDocument = JSON.parse(jsonContent);
const dataSourceInfo = processJson(jsonDocument);
// note: this should use the same function as used in modelServiceGenerator to avoid mismatches
const propertyKey = determinePropertyKey(jsonDocument, filePath, vfs, logger);
mergeDataSourceInfo(resultJson, propertyKey, dataSourceInfo);
}
catch (err) {
const error = err;
throw new Error(`Error processing file '${filePath}': ${error.message}`);
}
}
const outputJson = JSON.stringify(resultJson, null, 2); // pretty print
const finalOutput = `/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
* This file is auto-generated. Do not modify it manually.
* Changes to this file may be overwritten.
*/
export const dataSourcesInfo = ${outputJson};
`;
const outputDir = vfs.join(schemaFolderPath, 'appschemas');
const outputFilePath = vfs.join(outputDir, 'dataSourcesInfo.ts');
// Create the appschemas directory if it doesn't exist
if (!(await vfs.exists(outputDir))) {
await vfs.mkdir(outputDir, { recursive: true });
}
await vfs.writeFile(outputFilePath, finalOutput);
scenario.complete();
}
catch (error) {
scenario.failure({ error });
throw new Error(`An unexpected error occurred: ${error.message}`);
}
}
/**
* Merges new data source information into the result JSON object.
* If the property key already exists, it merges the APIs, avoiding duplicates.
* @param resultJson The result JSON object to merge into.
* @param propertyKey The key under which to store the data source information.
* @param newDataSourceInfo The new data source information to merge.
*/
function mergeDataSourceInfo(resultJson, propertyKey, newDataSourceInfo) {
if (Object.prototype.hasOwnProperty.call(resultJson, propertyKey)) {
const existingDataSource = resultJson[propertyKey];
if (existingDataSource && existingDataSource.apis) {
for (const apiKey in newDataSourceInfo.apis) {
if (!Object.prototype.hasOwnProperty.call(existingDataSource.apis, apiKey)) {
existingDataSource.apis[apiKey] = newDataSourceInfo.apis[apiKey];
}
}
}
else {
throw new Error(`The existing data source for key '${propertyKey}' is not of type DataSourceInfo.`);
}
}
else {
resultJson[propertyKey] = newDataSourceInfo;
}
}
/**
* Determines the property key for a data source, falling back to filename if extraction fails.
* @param jsonDocument The parsed JSON document.
* @param filePath The path to the JSON file.
* @param vfs The virtual file system instance.
* @param logger The logger instance for telemetry.
* @returns The property key to use for the data source.
*/
function determinePropertyKey(jsonDocument, filePath, vfs, logger) {
try {
return extractModelName(jsonDocument, filePath, vfs).dataSourceName.toLowerCase();
}
catch (error) {
logger.trackActivityEvent('DataSourceInfoProcessor_FallbackName', {
filePath,
error: error instanceof Error ? error.message : String(error),
});
// fall back to using the file name without extension
const fileName = vfs.basename(filePath);
return fileName.replace(FILE_EXTENSION_PATTERN, '').toLowerCase();
}
}
/**
* Processes the JSON document and extracts relevant data source information.
* @param json The JSON document to process.
* @param existingDataSourceInfo Optional existing data source information to merge with.
* @returns The processed data source information.
*/
function processJson(json, existingDataSourceInfo) {
const root = json;
if (typeof root.type === 'string' && root.type === 'Microsoft.PowerApps/apis') {
const dataSourceInfo = {
tableId: '',
version: '',
primaryKey: '',
dataSourceType: 'Connector',
apis: {},
};
const properties = isJsonObject(root.properties) ? root.properties : null;
const swagger = properties && isJsonObject(properties.swagger) ? properties.swagger : null;
const paths = swagger && isJsonObject(swagger.paths) ? swagger.paths : null;
if (paths) {
for (const [pathKey, pathValue] of Object.entries(paths)) {
if (isJsonObject(pathValue)) {
for (const [methodKey, methodValue] of Object.entries(pathValue)) {
if (isJsonObject(methodValue)) {
const operation = methodValue;
const operationId = operation.operationId;
if (typeof operationId === 'string') {
const apiDefinition = {
path: pathKey,
method: methodKey.toUpperCase(),
};
apiDefinition.parameters =
operation.parameters && Array.isArray(operation.parameters)
? processParameters(operation.parameters, swagger)
: [];
if (operation.responses) {
apiDefinition.responseInfo = processResponse(operation.responses, swagger);
}
else {
apiDefinition.responseInfo = {
default: { type: 'void', format: undefined },
};
}
dataSourceInfo.apis[operationId] = apiDefinition;
}
}
}
}
}
}
return dataSourceInfo;
}
else if (isJsonObject(root.schema)) {
const schema = root.schema;
if (isDataverseEntity(root)) {
const items = schema.items;
// This is a Dataverse entity schema
const primaryKey = typeof items['x-ms-dataverse-primary-id'] === 'string'
? items['x-ms-dataverse-primary-id']
: '';
return {
tableId: '',
version: '',
primaryKey,
dataSourceType: 'Dataverse',
apis: {},
};
}
if (typeof schema.procedureresultschema !== 'undefined') {
const dataSourceInfo = existingDataSourceInfo ?? {
tableId: '',
version: 'v2',
primaryKey: '',
dataSourceType: 'Connector',
apis: {},
};
const procedureName = typeof root.name === 'string' ? root.name : '';
const cleanedProcedureName = '/' + procedureName.replace(/\[|\]/g, '');
const cleanedProcedureNameWithoutSchema = getSqlStoredProcApiKey(procedureName);
let parameters = [];
if (isJsonObject(schema.inputparameters) && isJsonObject(schema.inputparameters.properties)) {
// Stored procedure parameters are wrapped under 'inputParameters' by the codegen.
// Use a single body param with that name so _buildOperationBodyParam can find the value.
parameters = [{ name: 'inputParameters', in: 'body', required: false, type: 'object' }];
}
const apiDefinition = {
path: cleanedProcedureName,
method: 'POST',
parameters,
};
if (!dataSourceInfo.apis[cleanedProcedureNameWithoutSchema]) {
dataSourceInfo.apis[cleanedProcedureNameWithoutSchema] = apiDefinition;
}
else {
throw new Error(`Duplicate stored procedure detected: ${cleanedProcedureName}`);
}
return dataSourceInfo;
}
else if (typeof root.name === 'string' && root.name.match(/^\[[^\]]+\]\./)) {
// Use the explicit version annotation injected at add-time when available.
// Fall back to 'v2' for backward compatibility with schema files created before this annotation was added.
const version = typeof root['x-ms-connection-version'] === 'string'
? root['x-ms-connection-version']
: 'v2';
return {
tableId: root.name,
version,
primaryKey: getPrimaryKeyFromSchema(schema),
dataSourceType: 'Connector',
apis: {},
};
}
else {
// Sharepoint List
const tableId = typeof root.name === 'string' && root.name !== null ? root.name : '';
const dataSourceInfo = {
tableId,
version: '',
primaryKey: getPrimaryKeyFromSchema(schema),
dataSourceType: 'Connector',
apis: {},
};
const referencedEntities = isJsonObject(root.referencedEntities)
? root.referencedEntities
: null;
if (referencedEntities) {
for (const referencedEntityKey in referencedEntities) {
if (!Object.prototype.hasOwnProperty.call(referencedEntities, referencedEntityKey)) {
continue;
}
const referencedEntityValue = referencedEntities[referencedEntityKey];
if (isJsonObject(referencedEntityValue) &&
referencedEntityValue.lookupEndpoint !== null) {
const lookupEndpointProps = referencedEntityValue.lookupEndpoint;
if (isJsonObject(lookupEndpointProps) && lookupEndpointProps.path !== null) {
const lookupEndpointPath = lookupEndpointProps.path;
if (typeof lookupEndpointPath === 'string') {
const segments = lookupEndpointPath.split('/');
// Process the segments as needed
segments[segments.length - 1] = referencedEntityKey; // Replace last segment
// in the updated segment remove hyphens in the GUIDs
// This might be an SPO connector issue, but we need to work around it here.
// ex: "/tables/05bfc50c-b51b-4c17-aac4-45bd5dbb9532/entities/Author",
// becomes "/tables/05bfc50cb51b4c17aac445bd5dbb9532/entities/Author"
const guidRegex = /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g;
const updatedSegment = segments
.join('/')
.replace(guidRegex, (match) => match.replace(/-/g, ''));
const finalPath = `/{connectionId}/datasets/{dataset}${updatedSegment}`;
const apiParameterList = new Array();
const defaultApiParameters = ['connectionId', 'dataset', 'table'];
const defaultQueryParameters = ['search'];
for (const param of defaultApiParameters) {
const apiParameter = {
name: param,
in: 'path',
required: true,
type: 'string',
};
apiParameterList.push(apiParameter);
}
for (const param of defaultQueryParameters) {
const apiParameter = {
name: param,
in: 'query',
required: false,
type: 'string',
};
apiParameterList.push(apiParameter);
}
const apiResponseInfo = {
'200': { type: 'array' },
};
const apiDefinition = {
path: finalPath,
method: 'GET',
parameters: apiParameterList,
responseInfo: apiResponseInfo,
};
const referencedEntityKeyNormalized = normalizeHyphenatedName(referencedEntityKey);
const operationId = `Get${referencedEntityKeyNormalized}`;
if (dataSourceInfo.apis[operationId]) {
throw new Error(`Duplicate operationId detected: ${operationId}`);
}
dataSourceInfo.apis[operationId] = apiDefinition;
}
}
}
}
}
return dataSourceInfo;
}
}
if (typeof root.type === 'string' && root.type === 'Microsoft.PowerApps/dataverseOperation') {
return processDataverseOperation(root);
}
throw new Error('The JSON does not represent a valid data source.');
}
/**
* Processes the responses node and extracts response information.
* @param responsesNode The responses node from the Swagger document.
* @param root The root of the Swagger document for reference.
* @returns A record of response information keyed by status code.
*/
function processResponse(responses, root) {
const result = {};
// Type guard: ensure responsesNode is an object
if (!isJsonObject(responses)) {
return result;
}
for (const statusCode in responses) {
if (!Object.prototype.hasOwnProperty.call(responses, statusCode)) {
continue;
}
const respObj = responses[statusCode];
// Type guard: ensure resp is an object
if (isJsonObject(respObj)) {
if (isJsonObject(respObj.schema)) {
const schemaNode = respObj.schema;
const type = typeof schemaNode.type === 'string' ? schemaNode.type : null;
const format = typeof schemaNode.format === 'string' ? schemaNode.format : null;
// If type is array, always return "array" as type
if (type === 'array') {
result[statusCode] = { type: 'array', format: format || undefined };
continue;
}
// $ref (single object)
// eslint-disable-next-line dot-notation
if (schemaNode['$ref']) {
// eslint-disable-next-line dot-notation
const refValue = schemaNode['$ref'];
if (typeof refValue === 'string' && refValue.startsWith('#/definitions/')) {
const defName = refValue.substring('#/definitions/'.length);
let refType = 'object';
// Type guard for root to access definitions
if (isJsonObject(root)) {
if (isJsonObject(root.definitions)) {
const definitions = root.definitions;
const defNode = definitions[defName];
if (isJsonObject(defNode)) {
refType = typeof defNode.type === 'string' ? defNode.type : 'object';
}
}
}
result[statusCode] = { type: refType, format: format || undefined };
continue;
}
}
// Primitive or object
result[statusCode] = { type: type || 'object', format: format || undefined };
}
else {
// No schema, but description exists
result[statusCode] = { type: 'void', format: undefined };
}
}
else {
// resp is not an object, treat as void
result[statusCode] = { type: 'void', format: undefined };
}
}
// If no responses at all, return a default void
if (Object.keys(result).length === 0) {
result.default = { type: 'void', format: undefined };
}
return result;
}
/**
* Retrieves the primary key from the schema node.
* @param schemaNode The schema node to extract the primary key from.
* @returns The primary key as a string, or an empty string if not found.
*/
function getPrimaryKeyFromSchema(schema) {
// Type guard: ensure schemaNode. items exists and is an object. properties exists and is an object
if (!isJsonObject(schema) ||
!isJsonObject(schema.items) ||
!isJsonObject(schema.items.properties)) {
return '';
}
const propertiesNode = schema.items.properties;
for (const propertyName in propertiesNode) {
if (!Object.prototype.hasOwnProperty.call(propertiesNode, propertyName)) {
continue;
}
const propertyValue = propertiesNode[propertyName];
// Type guard: ensure propertyValue is an object
if (isJsonObject(propertyValue)) {
if (propertyValue['x-ms-keyType'] === 'primary') {
return propertyName;
}
}
}
return '';
}
/**
* Processes the parameters node and extracts parameter information.
* @param parametersNode The parameters node from the Swagger document.
* @param root The root of the Swagger document for reference.
* @returns An array of ApiParameter objects representing the parameters.
*/
function processParameters(parametersNode, root) {
const parameters = [];
// Type guard: ensure parametersNode is an array
if (!Array.isArray(parametersNode)) {
return parameters;
}
for (const parameter of parametersNode) {
// Type guard: ensure parameter is an object
if (isJsonObject(parameter)) {
if (parameter.$ref) {
const refPath = parameter.$ref;
if (typeof refPath === 'string') {
const resolvedParameter = resolveRef(refPath, root);
if (resolvedParameter) {
parameters.push(parseParameter(resolvedParameter));
}
}
}
else {
parameters.push(parseParameter(parameter));
}
}
}
// Stable sort: required=true first, then required=false
return parameters.sort((a, b) => Number(b.required) - Number(a.required));
}
/**
* Resolves a $ref reference in the Swagger document.
* @param refPath The $ref path to resolve.
* @param root The root of the Swagger document for reference.
* @returns The resolved object or null if not found.
*/
function resolveRef(refPath, root) {
// Type guard: ensure root is an object
if (!isJsonObject(root)) {
return null;
}
// Resolve $ref (e.g., "#/swagger/parameters/ParameterName")
if (refPath.startsWith('#/')) {
const parts = refPath.substring(2).split('/');
let currentElement = root;
for (const part of parts) {
// Type guard: ensure currentElement is an object
if (isJsonObject(currentElement)) {
if (part in currentElement) {
currentElement = currentElement[part];
}
else {
return null; // Reference not found
}
}
else {
return null; // Current element is not an object
}
}
// Type guard: ensure final result is an object
if (isJsonObject(currentElement)) {
return currentElement;
}
}
return null;
}
/**
* Parses a parameter object and extracts relevant information.
* @param parameter The parameter object to parse.
* @returns An ApiParameter object representing the parsed parameter.
*/
function parseParameter(parameter) {
// In OpenAPI 2.0, body parameters encode type/format inside a "schema" sub-object.
// Path/query parameters use top-level "type"/"format" fields. Check both locations.
const schema = isJsonObject(parameter.schema) ? parameter.schema : null;
const type = typeof parameter.type === 'string'
? parameter.type
: typeof schema?.type === 'string'
? schema.type
: 'object';
const format = typeof parameter.format === 'string'
? parameter.format
: typeof schema?.format === 'string'
? schema.format
: undefined;
return {
name: typeof parameter.name === 'string' ? parameter.name : '',
in: typeof parameter.in === 'string' ? parameter.in : '',
required: parameter.required === true,
type,
format,
};
}
/*
* The normalizeHyphenatedName method normalizes hyphenated names.
* It removes all hyphens from the name and returns the normalized name.
*/
function normalizeHyphenatedName(name) {
return name.replace(/-/g, '');
}
/**
* Processes a Microsoft.PowerApps/dataverseOperation schema into a DataSourceInfo entry.
* Each operation is keyed by its lowercased operation name (e.g. `winopportunity`).
*
* Reads from the embedded Swagger 2.0 spec produced by `buildDataverseOperationSchema`.
* The swagger is nested under `properties.swagger` (connector schema format) so that the
* existing `handleSwaggerSchema` codegen path processes it without a separate handler.
* All type mappings are already resolved in the swagger at write-time.
*/
function processDataverseOperation(root) {
const props = isJsonObject(root.properties) ? root.properties : null;
const swagger = props && isJsonObject(props.swagger) ? props.swagger : null;
if (!swagger) {
throw new Error('Dataverse operation schema is missing the required "properties.swagger" field.');
}
const basePath = typeof swagger.basePath === 'string' ? swagger.basePath : '';
const paths = isJsonObject(swagger.paths) ? swagger.paths : null;
if (!paths) {
throw new Error('Dataverse operation schema swagger is missing "paths".');
}
const apis = {};
for (const [pathKey, pathValue] of Object.entries(paths)) {
if (!isJsonObject(pathValue))
continue;
for (const [methodKey, methodValue] of Object.entries(pathValue)) {
if (!isJsonObject(methodValue))
continue;
const operationId = typeof methodValue.operationId === 'string' ? methodValue.operationId : '';
if (!operationId)
continue;
const parameters = Array.isArray(methodValue.parameters)
? methodValue.parameters.filter(isJsonObject).map((param) => ({
name: typeof param.name === 'string' ? param.name : '',
in: typeof param.in === 'string' ? param.in : 'body',
required: param.required === true,
type: typeof param.type === 'string' ? param.type : 'object',
}))
: [];
apis[operationId] = {
path: basePath + pathKey,
method: methodKey.toUpperCase(),
parameters,
responseInfo: processResponse(methodValue.responses, swagger),
};
}
}
return {
tableId: '',
version: '',
primaryKey: '',
dataSourceType: 'Dataverse',
apis,
};
}
//# sourceMappingURL=dataSourceInfoProcessor.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,17 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import type { ILogger } from '../services/index.js';
export interface IGenerateModelServiceOptions {
schemaFolderPath: string;
schemaFilePath?: string;
codeGenPath: string;
logger: ILogger;
useV2Codegen?: boolean;
}
export declare function generateModelService(options: IGenerateModelServiceOptions): Promise<void>;
/**
* Clears and regenerates the model and service files.
*/
export declare function clearAndRegenerateModelService(options: IGenerateModelServiceOptions): Promise<void>;
//# sourceMappingURL=modelServiceGenerator.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"modelServiceGenerator.d.ts","sourceRoot":"","sources":["../../src/CodeGen/modelServiceGenerator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAuD3C,MAAM,WAAW,4BAA4B;IAC3C,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAYD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,IAAI,CAAC,CAoD/F;AAED;;GAEG;AACH,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAiBf"}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
/** Extracts a human-readable display name from CDP connector metadata, for use as the data source label. */
export declare function tryExtractDisplayNameFromCdpMetadata(metadata: unknown): string | undefined;
//# sourceMappingURL=cdpUtils.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"cdpUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/cdpUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,4GAA4G;AAC5G,wBAAgB,oCAAoC,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CA2B1F"}
@@ -0,0 +1,32 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import { isJsonObject } from './schemaUtils.js';
/** Extracts a human-readable display name from CDP connector metadata, for use as the data source label. */
export function tryExtractDisplayNameFromCdpMetadata(metadata) {
// Assuming metadata contains a 'name' or similar field for the stored procedure
try {
// If metadata is a string, try to parse it as JSON
let json = metadata;
if (typeof metadata === 'string') {
json = JSON.parse(metadata);
}
// Use type guards to safely access properties
if (isJsonObject(json)) {
// Try to get the title first, as it's often more user-friendly
if ('title' in json && typeof json.title === 'string') {
return json.title;
}
// Fall back to name if title isn't available
if ('name' in json && typeof json.name === 'string') {
return json.name;
}
}
// If we can't find a suitable property, use the default name
return undefined;
}
catch (ex) {
return undefined;
}
}
//# sourceMappingURL=cdpUtils.js.map
@@ -0,0 +1 @@
{"version":3,"file":"cdpUtils.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/cdpUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,4GAA4G;AAC5G,MAAM,UAAU,oCAAoC,CAAC,QAAiB;IACpE,gFAAgF;IAChF,IAAI,CAAC;QACH,mDAAmD;QACnD,IAAI,IAAI,GAAY,QAAQ,CAAC;QAC7B,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC;QAED,8CAA8C;QAC9C,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,+DAA+D;YAC/D,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACtD,OAAO,IAAI,CAAC,KAAK,CAAC;YACpB,CAAC;YAED,6CAA6C;YAC7C,IAAI,MAAM,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACpD,OAAO,IAAI,CAAC,IAAI,CAAC;YACnB,CAAC;QACH,CAAC;QAED,6DAA6D;QAC7D,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,EAAE,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,65 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import type { JsonObject } from '../../Common/jsonTypes.js';
/** Returns true if the schema root represents a Dataverse entity table. */
export declare function isDataverseEntity(root: unknown): boolean;
/** Returns the Dataverse entity set name for a schema (used as the OData API path), or null if not defined. */
export declare function getDataverseEntitySetName(root: unknown): string | null;
/** Returns true if the property is a Dataverse lookup relationship to another table. */
export declare function isDataverseLookupProperty(propValue: JsonObject): boolean;
/** Collects all lookup relationship columns from a properties node, keyed by property name. */
export declare function getDataverseLookupColumns(propertiesNode: JsonObject): Map<string, JsonObject>;
/** Returns true if the property is an auto-generated name field for a lookup column (e.g. `owneridname` for `ownerid`). */
export declare function isLookupRelatedProperty(propName: string, lookupColumns: Map<string, unknown>): boolean;
/** Returns the write-form property name for a lookup column, used when creating or updating Dataverse records. */
export declare function generateODataBindPropertyName(schemaName: string): string;
/** Returns the read-form property name for a lookup's resolved GUID value. */
export declare function generateLookupValuePropertyName(lookupPropertyName: string): string;
/**
* Returns true if the property is a Dataverse boolean (yes/no) field.
* These fields have enum/x-ms-enum-values in the schema but must be typed as `boolean`,
* not as a numeric optionset, because the Dataverse Web API expects true/false.
*/
export declare function isDataverseBooleanProperty(propValue: JsonObject): boolean;
/**
* Returns true if the property is a Dataverse numeric field (integer, decimal, double, money, bigint).
* These fields have `"type": "string"` in the schema but must be typed as `number` because
* the Dataverse Web API sends and receives them as JSON numbers.
*/
export declare function isDataverseNumericProperty(propValue: JsonObject): boolean;
/**
* Returns true if the property is a Dataverse multi-select picklist (multi-choice) field.
* These fields allow multiple selections and should be typed as an array of the enum type.
*/
export declare function isDataverseMultiSelectPicklistProperty(propValue: JsonObject): boolean;
/**
* Returns the names of all MultiSelectPicklist (multi-choice) properties in a Dataverse entity
* properties node — i.e., properties that have optionset data AND are typed as MultiSelectPicklistType.
* These fields require serialization (array → comma-separated string) when writing to the Dataverse API
* and deserialization (comma-separated string → array) when reading back from it.
*/
export declare function getDataverseMultiSelectPicklistFields(propertiesNode: JsonObject): string[];
/** Returns true if the property represents an optionset (choice) column with enumerable values. */
export declare function hasOptionsetData(propValue: JsonObject): boolean;
/** Produces the TypeScript identifier used for the enum or const generated from an optionset property. */
export declare function generateOptionsetEnumName(modelName: string, propName: string): string;
/** Formats an optionset display label as a TypeScript string literal for use in union types and const declarations. */
export declare function formatEnumLabel(label: unknown): string;
type GenericColumn = {
type: 'lookupRelated' | 'normal';
propName: string;
propValue: JsonObject;
readOnly: boolean;
};
type LookupColumn = {
type: 'lookup';
propName: string;
propValue: JsonObject;
readOnly: boolean;
schemaName: string;
};
/** Yields each property in a Dataverse entity's properties node, tagged by its role and mutability, for use in interface generation. */
export declare function iterateDataverseProperties(propertiesNode: JsonObject, lookupColumns: Map<string, JsonObject>): Generator<GenericColumn | LookupColumn>;
export {};
//# sourceMappingURL=dataverseUtils.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"dataverseUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/dataverseUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIzD,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAOxD;AAED,+GAA+G;AAC/G,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAQtE;AAED,wFAAwF;AACxF,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAExE;AAED,+FAA+F;AAC/F,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAQ7F;AAED,2HAA2H;AAC3H,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,OAAO,CAOT;AAED,kHAAkH;AAClH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,8EAA8E;AAC9E,wBAAgB,+BAA+B,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAElF;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAKzE;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAoBzE;AAED;;;GAGG;AACH,wBAAgB,sCAAsC,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAErF;AAED;;;;;GAKG;AACH,wBAAgB,qCAAqC,CAAC,cAAc,EAAE,UAAU,GAAG,MAAM,EAAE,CAa1F;AAED,mGAAmG;AACnG,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAK/D;AAED,0GAA0G;AAC1G,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErF;AAED,uHAAuH;AACvH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAStD;AAED,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,eAAe,GAAG,QAAQ,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,UAAU,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AACF,KAAK,YAAY,GAAG;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,UAAU,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wIAAwI;AACxI,wBAAiB,0BAA0B,CACzC,cAAc,EAAE,UAAU,EAC1B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GACrC,SAAS,CAAC,aAAa,GAAG,YAAY,CAAC,CAezC"}
@@ -0,0 +1,154 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import { convertToValidIdentifier } from './nameUtils.js';
import { isJsonObject } from './schemaUtils.js';
/** Returns true if the schema root represents a Dataverse entity table. */
export function isDataverseEntity(root) {
return (isJsonObject(root) &&
isJsonObject(root.schema) &&
isJsonObject(root.schema.items) &&
root.schema.items['x-ms-dataverse-entity'] === true);
}
/** Returns the Dataverse entity set name for a schema (used as the OData API path), or null if not defined. */
export function getDataverseEntitySetName(root) {
if (isJsonObject(root) && isJsonObject(root.schema) && isJsonObject(root.schema.items)) {
const entitySetName = root.schema.items['x-ms-dataverse-entityset'];
if (typeof entitySetName === 'string' && entitySetName.trim()) {
return entitySetName;
}
}
return null;
}
/** Returns true if the property is a Dataverse lookup relationship to another table. */
export function isDataverseLookupProperty(propValue) {
return propValue['x-ms-dataverse-type'] === 'LookupType';
}
/** Collects all lookup relationship columns from a properties node, keyed by property name. */
export function getDataverseLookupColumns(propertiesNode) {
const lookupColumns = new Map();
for (const [propName, propValue] of Object.entries(propertiesNode)) {
if (isJsonObject(propValue) && isDataverseLookupProperty(propValue)) {
lookupColumns.set(propName, propValue);
}
}
return lookupColumns;
}
/** Returns true if the property is an auto-generated name field for a lookup column (e.g. `owneridname` for `ownerid`). */
export function isLookupRelatedProperty(propName, lookupColumns) {
for (const lookupPropName of lookupColumns.keys()) {
if (propName === `${lookupPropName}name`) {
return true;
}
}
return false;
}
/** Returns the write-form property name for a lookup column, used when creating or updating Dataverse records. */
export function generateODataBindPropertyName(schemaName) {
return `${schemaName}@odata.bind`;
}
/** Returns the read-form property name for a lookup's resolved GUID value. */
export function generateLookupValuePropertyName(lookupPropertyName) {
return `_${lookupPropertyName}_value`;
}
/**
* Returns true if the property is a Dataverse boolean (yes/no) field.
* These fields have enum/x-ms-enum-values in the schema but must be typed as `boolean`,
* not as a numeric optionset, because the Dataverse Web API expects true/false.
*/
export function isDataverseBooleanProperty(propValue) {
return (propValue['x-ms-dataverse-type'] === 'BooleanType' ||
propValue['x-ms-optionSetType'] === 'Boolean');
}
/**
* Returns true if the property is a Dataverse numeric field (integer, decimal, double, money, bigint).
* These fields have `"type": "string"` in the schema but must be typed as `number` because
* the Dataverse Web API sends and receives them as JSON numbers.
*/
export function isDataverseNumericProperty(propValue) {
const dvType = typeof propValue['x-ms-dataverse-type'] === 'string'
? propValue['x-ms-dataverse-type'].toLowerCase()
: undefined;
switch (dvType) {
case 'integer':
case 'integertype':
case 'bigint':
case 'biginttype':
case 'decimal':
case 'decimaltype':
case 'double':
case 'doubletype':
case 'money':
case 'moneytype':
return true;
default:
return false;
}
}
/**
* Returns true if the property is a Dataverse multi-select picklist (multi-choice) field.
* These fields allow multiple selections and should be typed as an array of the enum type.
*/
export function isDataverseMultiSelectPicklistProperty(propValue) {
return propValue['x-ms-dataverse-type'] === 'MultiSelectPicklistType';
}
/**
* Returns the names of all MultiSelectPicklist (multi-choice) properties in a Dataverse entity
* properties node — i.e., properties that have optionset data AND are typed as MultiSelectPicklistType.
* These fields require serialization (array → comma-separated string) when writing to the Dataverse API
* and deserialization (comma-separated string → array) when reading back from it.
*/
export function getDataverseMultiSelectPicklistFields(propertiesNode) {
const fields = [];
for (const [propName, propValue] of Object.entries(propertiesNode)) {
if (isJsonObject(propValue) &&
isDataverseMultiSelectPicklistProperty(propValue) &&
Array.isArray(propValue.enum) &&
propValue.enum.length > 0) {
fields.push(propName);
}
}
return fields;
}
/** Returns true if the property represents an optionset (choice) column with enumerable values. */
export function hasOptionsetData(propValue) {
if (isDataverseBooleanProperty(propValue)) {
return false;
}
return Array.isArray(propValue.enum) && propValue.enum.length > 0;
}
/** Produces the TypeScript identifier used for the enum or const generated from an optionset property. */
export function generateOptionsetEnumName(modelName, propName) {
return convertToValidIdentifier(`${modelName}${propName}`);
}
/** Formats an optionset display label as a TypeScript string literal for use in union types and const declarations. */
export function formatEnumLabel(label) {
if (label === null || label === undefined) {
return "''";
}
const str = String(label);
if (str.trim() === '') {
return "''";
}
return `'${str.replace(/'/g, "\\'")}'`;
}
/** Yields each property in a Dataverse entity's properties node, tagged by its role and mutability, for use in interface generation. */
export function* iterateDataverseProperties(propertiesNode, lookupColumns) {
for (const [propName, propValue] of Object.entries(propertiesNode)) {
if (isJsonObject(propValue)) {
const readOnly = !!propValue['x-ms-read-only'];
if (lookupColumns.has(propName)) {
const schemaNameRaw = propValue['x-ms-schema-name'];
const schemaName = typeof schemaNameRaw === 'string' ? schemaNameRaw : propName;
yield { type: 'lookup', readOnly, propName, propValue, schemaName };
}
else if (isLookupRelatedProperty(propName, lookupColumns)) {
yield { type: 'lookupRelated', readOnly, propName, propValue };
}
else {
yield { type: 'normal', readOnly, propName, propValue };
}
}
}
}
//# sourceMappingURL=dataverseUtils.js.map
@@ -0,0 +1 @@
{"version":3,"file":"dataverseUtils.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/dataverseUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB,CAAC,IAAa;IAC7C,OAAO,CACL,YAAY,CAAC,IAAI,CAAC;QAClB,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;QACzB,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,KAAK,IAAI,CACpD,CAAC;AACJ,CAAC;AAED,+GAA+G;AAC/G,MAAM,UAAU,yBAAyB,CAAC,IAAa;IACrD,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACvF,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QACpE,IAAI,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9D,OAAO,aAAa,CAAC;QACvB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,yBAAyB,CAAC,SAAqB;IAC7D,OAAO,SAAS,CAAC,qBAAqB,CAAC,KAAK,YAAY,CAAC;AAC3D,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,yBAAyB,CAAC,cAA0B;IAClE,MAAM,aAAa,GAAG,IAAI,GAAG,EAAsB,CAAC;IACpD,KAAK,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QACnE,IAAI,YAAY,CAAC,SAAS,CAAC,IAAI,yBAAyB,CAAC,SAAS,CAAC,EAAE,CAAC;YACpE,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,2HAA2H;AAC3H,MAAM,UAAU,uBAAuB,CACrC,QAAgB,EAChB,aAAmC;IAEnC,KAAK,MAAM,cAAc,IAAI,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC;QAClD,IAAI,QAAQ,KAAK,GAAG,cAAc,MAAM,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kHAAkH;AAClH,MAAM,UAAU,6BAA6B,CAAC,UAAkB;IAC9D,OAAO,GAAG,UAAU,aAAa,CAAC;AACpC,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,+BAA+B,CAAC,kBAA0B;IACxE,OAAO,IAAI,kBAAkB,QAAQ,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,SAAqB;IAC9D,OAAO,CACL,SAAS,CAAC,qBAAqB,CAAC,KAAK,aAAa;QAClD,SAAS,CAAC,oBAAoB,CAAC,KAAK,SAAS,CAC9C,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,SAAqB;IAC9D,MAAM,MAAM,GACV,OAAO,SAAS,CAAC,qBAAqB,CAAC,KAAK,QAAQ;QAClD,CAAC,CAAE,SAAS,CAAC,qBAAqB,CAAY,CAAC,WAAW,EAAE;QAC5D,CAAC,CAAC,SAAS,CAAC;IAChB,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,SAAS,CAAC;QACf,KAAK,aAAa,CAAC;QACnB,KAAK,QAAQ,CAAC;QACd,KAAK,YAAY,CAAC;QAClB,KAAK,SAAS,CAAC;QACf,KAAK,aAAa,CAAC;QACnB,KAAK,QAAQ,CAAC;QACd,KAAK,YAAY,CAAC;QAClB,KAAK,OAAO,CAAC;QACb,KAAK,WAAW;YACd,OAAO,IAAI,CAAC;QACd;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sCAAsC,CAAC,SAAqB;IAC1E,OAAO,SAAS,CAAC,qBAAqB,CAAC,KAAK,yBAAyB,CAAC;AACxE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qCAAqC,CAAC,cAA0B;IAC9E,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QACnE,IACE,YAAY,CAAC,SAAS,CAAC;YACvB,sCAAsC,CAAC,SAAS,CAAC;YACjD,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC;YAC7B,SAAS,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EACzB,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,gBAAgB,CAAC,SAAqB;IACpD,IAAI,0BAA0B,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;AACpE,CAAC;AAED,0GAA0G;AAC1G,MAAM,UAAU,yBAAyB,CAAC,SAAiB,EAAE,QAAgB;IAC3E,OAAO,wBAAwB,CAAC,GAAG,SAAS,GAAG,QAAQ,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,uHAAuH;AACvH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC;AACzC,CAAC;AAgBD,wIAAwI;AACxI,MAAM,SAAS,CAAC,CAAC,0BAA0B,CACzC,cAA0B,EAC1B,aAAsC;IAEtC,KAAK,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QACnE,IAAI,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;YAC/C,IAAI,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChC,MAAM,aAAa,GAAG,SAAS,CAAC,kBAAkB,CAAC,CAAC;gBACpD,MAAM,UAAU,GAAG,OAAO,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAChF,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;YACtE,CAAC;iBAAM,IAAI,uBAAuB,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,CAAC;gBAC5D,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;YACjE,CAAC;iBAAM,CAAC;gBACN,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;YAC1D,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,24 @@
import type { JsonObject } from '../../Common/jsonTypes.js';
import type { ReadOnlyVfs } from '../../Types/Vfs.types.js';
/**
* Derives the model name and data source name from a schema.
* Handles Dataverse entities, shared connectors, GUIDs, SQL stored procedures, and generic connectors.
*/
export declare function extractModelName(schema: unknown, schemaPath: string, vfs: ReadOnlyVfs): {
modelName: string;
dataSourceName: string;
};
/**
* Generates a compact single-line TypeScript object type for use inside `allOf` intersection types.
* Returns a type expression like `{ id: string; name?: string; }`.
*/
export declare function generateCompactInlineObjectType(modelName: string, definition: JsonObject): string;
/**
* Generates an inline TypeScript object type string for a schema node that has nested `properties`.
* Returns a type expression like `{ street?: string; city?: string; }` suitable for use as a
* property type in both V1 string output and V2 ts-morph `type` fields.
*/
export declare function generateInlineObjectTypeString(modelName: string, definition: JsonObject, padding: string): string;
/** Resolves the TypeScript type for a schema property, dispatching across optionsets, arrays, nested objects, refs, and primitives. */
export declare function determineTypeScriptType(modelName: string, propValue: JsonObject | undefined, padding: string, propName?: string): string;
//# sourceMappingURL=modelUtils.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"modelUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/modelUtils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AA2BzD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,OAAO,EACf,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,WAAW,GACf;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAkC/C;AAqDD;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAmBjG;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,MAAM,GACd,MAAM,CAmBR;AAED,uIAAuI;AACvI,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,UAAU,GAAG,SAAS,EACjC,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAmCR"}
@@ -0,0 +1,185 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import { isGuid } from '@microsoft/power-apps-common/utils';
import { generateOptionsetEnumName, getDataverseEntitySetName, hasOptionsetData, isDataverseBooleanProperty, isDataverseEntity, isDataverseMultiSelectPicklistProperty, isDataverseNumericProperty, } from './dataverseUtils.js';
import { convertToValidIdentifier, formatPropertyName, normalizePropertyName, sanitizeName, sanitizeTabularResourceName, } from './nameUtils.js';
import { extractReferencedType, getNameNode, getPropertiesNode, getRequiredFields, isJsonObject, mapJsonTypeToTypeScript, } from './schemaUtils.js';
import { getParentFolderName, isSqlStoredProcedure } from './sqlUtils.js';
/**
* Derives the model name and data source name from a schema.
* Handles Dataverse entities, shared connectors, GUIDs, SQL stored procedures, and generic connectors.
*/
export function extractModelName(schema, schemaPath, vfs) {
if (!schema) {
throw new Error('Schema cannot be null.');
}
// Check if this is a Dataverse entity schema
if (isDataverseEntity(schema)) {
const entitySetName = getDataverseEntitySetName(schema);
if (entitySetName) {
const modelName = capitalizeForFileName(sanitizeName(entitySetName));
return { modelName, dataSourceName: entitySetName.trim() };
}
}
const nameNode = getNameNode(schema);
let modelName;
let dataSourceName;
if (isSharedName(nameNode)) {
modelName = getDisplayName(schema);
dataSourceName = getSharedDataSourceName(nameNode);
}
else if (isGuid(nameNode)) {
modelName = getTitle(schema);
dataSourceName = modelName;
}
else if (isSqlStoredProcedure(schema)) {
modelName = sanitizeTabularResourceName(nameNode);
dataSourceName = getParentFolderName(schemaPath, vfs);
}
else {
modelName = sanitizeTabularResourceName(nameNode);
dataSourceName = modelName;
}
modelName = capitalizeForFileName(sanitizeName(modelName));
return { modelName, dataSourceName };
}
function capitalizeForFileName(name) {
if (!name) {
return name;
}
return name.charAt(0).toUpperCase() + name.slice(1);
}
function getDisplayName(schema) {
if (isJsonObject(schema) && isJsonObject(schema.properties)) {
const displayName = schema.properties.displayName;
if (typeof displayName !== 'string' || !displayName.trim()) {
throw new Error("No 'displayName' found in schema properties");
}
return displayName;
}
throw new Error('Schema must be a JSON object with properties to extract displayName');
}
function isSharedName(nameNode) {
return nameNode.toLowerCase().startsWith('shared_');
}
function getSharedDataSourceName(nameNode) {
return sanitizeName(nameNode.replace(/^shared_/i, '')).toLowerCase();
}
function getTitle(schema) {
if (isJsonObject(schema)) {
const title = schema.title;
if (typeof title !== 'string' || !title.trim()) {
throw new Error("No 'title' found in schema");
}
return title;
}
throw new Error('Schema must be a JSON object to extract title');
}
function addPropertyComment(lines, propValue, padding) {
if (propValue?.description && typeof propValue.description === 'string') {
// Split multi-line descriptions so every line is prefixed with '//'
const commentLines = propValue.description
.split(/\r?\n/)
.map((line) => `${padding} // ${line}`);
lines.push(...commentLines);
}
}
/**
* Generates a compact single-line TypeScript object type for use inside `allOf` intersection types.
* Returns a type expression like `{ id: string; name?: string; }`.
*/
export function generateCompactInlineObjectType(modelName, definition) {
const requiredFields = getRequiredFields(definition);
const propertiesNode = getPropertiesNode(definition);
const parts = [];
for (const [propName, propValue] of Object.entries(propertiesNode)) {
// Use formatPropertyName(propName) directly to preserve the original JSON key.
// Do NOT pass through convertToValidIdentifier here — that's for identifier names
// (type aliases, variable names), not property keys. e.g. "@odata.type" must stay
// as `"@odata.type"` in the emitted interface to match the JSON wire format.
let formattedPropName = formatPropertyName(propName);
formattedPropName = normalizePropertyName(formattedPropName);
const propValueAsObject = isJsonObject(propValue) ? propValue : undefined;
const tsType = determineTypeScriptType(modelName, propValueAsObject, '', propName);
const optionalMark = requiredFields.has(propName) ? '' : '?';
parts.push(`${formattedPropName}${optionalMark}: ${tsType}`);
}
return `{ ${parts.join('; ')} }`;
}
/**
* Generates an inline TypeScript object type string for a schema node that has nested `properties`.
* Returns a type expression like `{ street?: string; city?: string; }` suitable for use as a
* property type in both V1 string output and V2 ts-morph `type` fields.
*/
export function generateInlineObjectTypeString(modelName, definition, padding) {
const requiredFields = getRequiredFields(definition);
const propertiesNode = getPropertiesNode(definition);
const lines = ['{'];
for (const [propName, propValue] of Object.entries(propertiesNode)) {
// Use formatPropertyName(propName) directly — same reason as generateCompactInlineObjectType:
// property keys must match the JSON wire format, not be sanitized into identifier names.
let formattedPropName = formatPropertyName(propName);
formattedPropName = normalizePropertyName(formattedPropName);
const propValueAsObject = isJsonObject(propValue) ? propValue : undefined;
const tsType = determineTypeScriptType(modelName, propValueAsObject, padding, propName);
const optionalMark = requiredFields.has(propName) ? '' : '?';
addPropertyComment(lines, propValueAsObject, padding);
lines.push(`${padding} ${formattedPropName}${optionalMark}: ${tsType};`);
}
lines.push(`${padding}}`);
return lines.join('\n');
}
/** Resolves the TypeScript type for a schema property, dispatching across optionsets, arrays, nested objects, refs, and primitives. */
export function determineTypeScriptType(modelName, propValue, padding, propName) {
// Dataverse boolean (yes/no) fields must be typed as `boolean`, not as a numeric optionset.
// The Dataverse Web API expects true/false, not 0/1.
if (propValue && isDataverseBooleanProperty(propValue)) {
return 'boolean';
}
// Dataverse numeric fields (integer, decimal, double, money, bigint) have `"type": "string"`
// in the schema but the Dataverse Web API sends/receives them as JSON numbers.
if (propValue && isDataverseNumericProperty(propValue)) {
return 'number';
}
// Check if this property has optionset data first
if (propValue && propName && hasOptionsetData(propValue)) {
const enumName = generateOptionsetEnumName(modelName, propName);
// Multi-select picklists allow multiple selections, so they are typed as arrays.
return isDataverseMultiSelectPicklistProperty(propValue) ? `${enumName}[]` : enumName;
}
if (propValue?.type === 'array' && isJsonObject(propValue.items)) {
return determineArrayType(modelName, propValue.items, padding, propName);
}
else if (propValue?.properties && typeof propValue.properties === 'object') {
// Use modelName + propName as the sub-model name so that nested enum type aliases
// are unique even when two sibling inline objects have a property with the same name
// (e.g. both `phoneNumbers.type` and `emailAddresses.type` → previously both got
// `Createperson_Requesttype`, now they get `Createperson_RequestphoneNumberstype`
// and `Createperson_RequestemailAddressestype`).
const subModelName = propName ? `${modelName}${convertToValidIdentifier(propName)}` : modelName;
return generateInlineObjectTypeString(subModelName, propValue, padding + ' ');
}
else if (typeof propValue?.$ref === 'string') {
return extractReferencedType(propValue.$ref);
}
else {
return mapJsonTypeToTypeScript(propValue?.type);
}
}
/** Resolves the TypeScript element type for an array property. */
function determineArrayType(modelName, itemsNode, padding, propName) {
if ('properties' in itemsNode && typeof itemsNode.properties === 'object') {
const subModelName = propName ? `${modelName}${convertToValidIdentifier(propName)}` : modelName;
const inline = generateInlineObjectTypeString(subModelName, itemsNode, padding + ' ');
return `${inline}[]`;
}
else if ('$ref' in itemsNode && typeof itemsNode.$ref === 'string') {
return `${extractReferencedType(itemsNode.$ref)}[]`;
}
else {
const itemType = ('type' in itemsNode ? itemsNode.type : 'unknown');
return mapJsonTypeToTypeScript(itemType) + '[]';
}
}
//# sourceMappingURL=modelUtils.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,33 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
/** Converts an arbitrary string to a safe TypeScript identifier for use in generated code. */
export declare function convertToValidIdentifier(variableOrInterfaceName: string): string;
/** Sanitizes a raw string to a valid TypeScript identifier, for use when generating type and variable names. */
export declare function sanitizeName(name: string, removeWhitespace?: boolean, prefixReservedKeywords?: boolean): string;
/** Sanitizes a property name for use in a TypeScript interface, without prefixing reserved keywords. */
export declare function sanitizePropertyName(name: string): string;
/** Returns a unique sanitized name, appending a numeric suffix to avoid collisions with existing names. */
export declare function getSanitizedUniqueName(existingNames: string[], desiredName: string): string;
/** Normalizes a type name containing generic bracket syntax into a form suitable as a TypeScript identifier. */
export declare function normalizeGenericTypeName(typeName: string): string;
/**
* Extracts a clean identifier from a potentially schema-qualified resource name,
* for use in generated TypeScript identifiers.
*
* Handles bracket-qualified names used by SQL, Oracle, and other tabular connectors
* (e.g. `[dbo].[TableName]` or `[OWNER].[TABLE_NAME]`), as well as plain names.
* Always takes the last segment after the final dot, strips brackets, then sanitizes.
*/
export declare function sanitizeTabularResourceName(name: string): string;
/** Returns the data source name accessor for the given model's service class. */
export declare function getServiceName(modelName: string): string;
/** Applies well-known casing fixes to property names before they are emitted into generated types. */
export declare function normalizePropertyName(propertyName: string): string;
/** Converts a kebab-case string to camelCase (e.g., `common-models` → `commonModels`). */
export declare function kebabToCamelCase(name: string): string;
/** Wraps a property name in quotes when it cannot appear as a bare TypeScript identifier. */
export declare function formatPropertyName(propName: string): string;
/** Returns true if a property name can be used without quotes in a TypeScript type definition. */
export declare function isValidPropertyName(propName: string): boolean;
//# sourceMappingURL=nameUtils.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"nameUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/nameUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAkEH,8FAA8F;AAC9F,wBAAgB,wBAAwB,CAAC,uBAAuB,EAAE,MAAM,UAOvE;AAED,gHAAgH;AAChH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,gBAAgB,GAAE,OAAc,EAChC,sBAAsB,GAAE,OAAc,GACrC,MAAM,CA8BR;AAED,wGAAwG;AACxG,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKzD;AAED,2GAA2G;AAC3G,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAsB3F;AAED,gHAAgH;AAChH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOhE;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED,sGAAsG;AACtG,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,6FAA6F;AAC7F,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM3D;AAED,kGAAkG;AAClG,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAI7D"}
@@ -0,0 +1,180 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
// TypeScript reserved keywords and built-in types
const RESERVED_KEYWORDS = new Set([
'break',
'case',
'catch',
'class',
'const',
'continue',
'debugger',
'default',
'delete',
'do',
'else',
'enum',
'export',
'extends',
'false',
'finally',
'for',
'function',
'if',
'import',
'in',
'instanceof',
'new',
'null',
'return',
'super',
'switch',
'this',
'throw',
'true',
'try',
'typeof',
'var',
'void',
'while',
'with',
'as',
'implements',
'interface',
'let',
'package',
'private',
'protected',
'public',
'static',
'yield',
'any',
'boolean',
'constructor',
'declare',
'get',
'module',
'require',
'number',
'set',
'string',
'symbol',
'type',
'from',
'of',
]);
/** Converts an arbitrary string to a safe TypeScript identifier for use in generated code. */
export function convertToValidIdentifier(variableOrInterfaceName) {
if (variableOrInterfaceName.indexOf('@odata') > -1) {
// Strip leading '@' and replace '.' with '_' to produce a valid identifier, e.g. "@odata.nextLink" -> "odata_nextLink"
return variableOrInterfaceName.replace(/^@/, '').replace(/\./g, '_');
}
return sanitizeName(variableOrInterfaceName, false);
}
/** Sanitizes a raw string to a valid TypeScript identifier, for use when generating type and variable names. */
export function sanitizeName(name, removeWhitespace = true, prefixReservedKeywords = true) {
const originalName = name;
// Remove any leading or trailing whitespace
name = name.trim();
if (!name) {
throw new Error('Invalid identifier: empty string');
}
if (removeWhitespace) {
name = name.replace(/\s/g, '');
}
// Remove any characters that are not alphanumeric or underscores
name = name.replace(/[^a-zA-Z0-9_$]/g, '_');
// Ensure the name does not start with a number
if (/^\d/.test(name)) {
name = `_${name}`;
}
// Ensure the name is not empty or all underscores
if (name.length === 0 || /^_+$/.test(name)) {
throw new Error(`Failed to sanitize string ${originalName}`);
}
if (prefixReservedKeywords && RESERVED_KEYWORDS.has(name)) {
name = `_${name}`;
}
return name;
}
/** Sanitizes a property name for use in a TypeScript interface, without prefixing reserved keywords. */
export function sanitizePropertyName(name) {
if (name.indexOf('@odata') > -1) {
return `"${name}"`;
}
return sanitizeName(name, false, false);
}
/** Returns a unique sanitized name, appending a numeric suffix to avoid collisions with existing names. */
export function getSanitizedUniqueName(existingNames, desiredName) {
// Sanitize the base name
desiredName = sanitizeName(desiredName);
const nameSet = new Set(existingNames);
if (!nameSet.has(desiredName)) {
return desiredName.toLowerCase();
}
let maxSuffix = 0;
// Non-literal regex shoud be safe here since baseName is sanitized
// and should not contain any special characters that could lead to regex injection.
const regex = new RegExp(`^${desiredName}_(\\d+)$`);
for (const name of existingNames) {
const match = name.match(regex);
if (match) {
const num = parseInt(match[1], 10);
if (num > maxSuffix) {
maxSuffix = num;
}
}
}
return `${desiredName}_${maxSuffix + 1}`.toLowerCase();
}
/** Normalizes a type name containing generic bracket syntax into a form suitable as a TypeScript identifier. */
export function normalizeGenericTypeName(typeName) {
return typeName.replace(/\[/g, '_').replace(/\]/g, '');
}
/**
* Extracts a clean identifier from a potentially schema-qualified resource name,
* for use in generated TypeScript identifiers.
*
* Handles bracket-qualified names used by SQL, Oracle, and other tabular connectors
* (e.g. `[dbo].[TableName]` or `[OWNER].[TABLE_NAME]`), as well as plain names.
* Always takes the last segment after the final dot, strips brackets, then sanitizes.
*/
export function sanitizeTabularResourceName(name) {
const result = name.trim().replace(/\[|\]/g, '');
const match = result.match(/\.?([^.]*)$/);
if (match) {
return sanitizeName(match[1]);
}
return sanitizeName(result);
}
/** Returns the data source name accessor for the given model's service class. */
export function getServiceName(modelName) {
if (!modelName || !modelName.trim()) {
throw new Error('Model name cannot be null or empty.');
}
return `${modelName}Service.dataSourceName`;
}
/** Applies well-known casing fixes to property names before they are emitted into generated types. */
export function normalizePropertyName(propertyName) {
return propertyName.toLowerCase() === 'resultsets' ? 'ResultSets' : propertyName;
}
/** Converts a kebab-case string to camelCase (e.g., `common-models` → `commonModels`). */
export function kebabToCamelCase(name) {
return name.replace(/-+(.)/g, (_, c) => c.toUpperCase()).replace(/-+$/, '');
}
/** Wraps a property name in quotes when it cannot appear as a bare TypeScript identifier. */
export function formatPropertyName(propName) {
// Skip if already quoted
if (propName.startsWith('"') && propName.endsWith('"')) {
return propName;
}
return isValidPropertyName(propName) ? propName : `"${propName}"`;
}
/** Returns true if a property name can be used without quotes in a TypeScript type definition. */
export function isValidPropertyName(propName) {
// A valid property name must start with a letter, underscore, or dollar sign
// and can only contain letters, numbers, underscores, or dollar signs
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(propName);
}
//# sourceMappingURL=nameUtils.js.map
@@ -0,0 +1 @@
{"version":3,"file":"nameUtils.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/nameUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,kDAAkD;AAClD,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IAChC,OAAO;IACP,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;IACP,UAAU;IACV,UAAU;IACV,SAAS;IACT,QAAQ;IACR,IAAI;IACJ,MAAM;IACN,MAAM;IACN,QAAQ;IACR,SAAS;IACT,OAAO;IACP,SAAS;IACT,KAAK;IACL,UAAU;IACV,IAAI;IACJ,QAAQ;IACR,IAAI;IACJ,YAAY;IACZ,KAAK;IACL,MAAM;IACN,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,MAAM;IACN,OAAO;IACP,MAAM;IACN,KAAK;IACL,QAAQ;IACR,KAAK;IACL,MAAM;IACN,OAAO;IACP,MAAM;IACN,IAAI;IACJ,YAAY;IACZ,WAAW;IACX,KAAK;IACL,SAAS;IACT,SAAS;IACT,WAAW;IACX,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,KAAK;IACL,SAAS;IACT,aAAa;IACb,SAAS;IACT,KAAK;IACL,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,KAAK;IACL,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,MAAM;IACN,IAAI;CACL,CAAC,CAAC;AAEH,8FAA8F;AAC9F,MAAM,UAAU,wBAAwB,CAAC,uBAA+B;IACtE,IAAI,uBAAuB,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACnD,uHAAuH;QACvH,OAAO,uBAAuB,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,YAAY,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;AACtD,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,mBAA4B,IAAI,EAChC,yBAAkC,IAAI;IAEtC,MAAM,YAAY,GAAG,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAEnB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,gBAAgB,EAAE,CAAC;QACrB,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,iEAAiE;IACjE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;IAC5C,+CAA+C;IAC/C,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;IACpB,CAAC;IAED,kDAAkD;IAClD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,6BAA6B,YAAY,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,IAAI,sBAAsB,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1D,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;IACpB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,IAAI,GAAG,CAAC;IACrB,CAAC;IACD,OAAO,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAC1C,CAAC;AAED,2GAA2G;AAC3G,MAAM,UAAU,sBAAsB,CAAC,aAAuB,EAAE,WAAmB;IACjF,yBAAyB;IACzB,WAAW,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IAExC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;QAC9B,OAAO,WAAW,CAAC,WAAW,EAAE,CAAC;IACnC,CAAC;IACD,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,mEAAmE;IACnE,oFAAoF;IACpF,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,WAAW,UAAU,CAAC,CAAC;IACpD,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,GAAG,GAAG,SAAS,EAAE,CAAC;gBACpB,SAAS,GAAG,GAAG,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,WAAW,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;AACzD,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,wBAAwB,CAAC,QAAgB;IACvD,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CAAC,IAAY;IACtD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC1C,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,GAAG,SAAS,wBAAwB,CAAC;AAC9C,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,qBAAqB,CAAC,YAAoB;IACxD,OAAO,YAAY,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;AACnF,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACtF,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,yBAAyB;IACzB,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvD,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,GAAG,CAAC;AACpE,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,6EAA6E;IAC7E,sEAAsE;IACtE,OAAO,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,42 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import type { ILogger } from '../../services/index.js';
import type { Vfs } from '../../Types/Vfs.types.js';
/**
* Compatibility layer for V1 and V2 codegen projects.
*/
export interface ICodegenProject {
/** Create a file at the given path with the given content. */
createFile(identifier: FileIdentifier, filePath: string, content: string): Promise<unknown>;
/** Commit any uncommmited files to the filesystem */
commit(): Promise<void>;
/** Join path segments using the underlying path convention. */
join(...paths: string[]): string;
/** Returns true if a directory exists at the given path. */
directoryExists(path: string): Promise<boolean>;
/** Returns true if a file exists at the given path. */
fileExists(path: string): Promise<boolean>;
/** Returns the names of all source files directly inside the given directory. */
getSourceFilesInDirectory(path: string): Promise<string[]>;
}
/**
* The types of files that can be generated by codegen. Each project can have up to one of each.
*/
export type FileIdentifier = 'model' | 'service' | 'commonModel' | 'index';
/**
* Compatibility layer for V1 and V2 codegen projects. The purpose of this class is
* purely to preserve existing V1 codegen logic as closeley as possible.
*/
export declare class CodegenV1Project implements ICodegenProject {
private vfs;
private logger;
constructor(vfs: Vfs, logger: ILogger);
createFile(_identifier: FileIdentifier, filePath: string, content: string): Promise<void>;
commit(): Promise<void>;
join(...paths: string[]): string;
directoryExists(path: string): Promise<boolean>;
fileExists(path: string): Promise<boolean>;
getSourceFilesInDirectory(path: string): Promise<string[]>;
}
//# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/project.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,UAAU,CAAC,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5F,qDAAqD;IACrD,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,+DAA+D;IAC/D,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IACjC,4DAA4D;IAC5D,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,uDAAuD;IACvD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,iFAAiF;IACjF,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC5D;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,aAAa,GAAG,OAAO,CAAC;AAE3E;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,eAAe;IAEpD,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,MAAM;gBADN,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,OAAO;IAGnB,UAAU,CAAC,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASzF,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM;IAIhC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI/C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIpC,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAGjE"}
@@ -0,0 +1,39 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
/**
* Compatibility layer for V1 and V2 codegen projects. The purpose of this class is
* purely to preserve existing V1 codegen logic as closeley as possible.
*/
export class CodegenV1Project {
vfs;
logger;
constructor(vfs, logger) {
this.vfs = vfs;
this.logger = logger;
}
async createFile(_identifier, filePath, content) {
// V1 codegen does not use the identifiers and relies in finding files by their path.
const dir = this.vfs.dirname(filePath);
if (!(await this.vfs.exists(dir))) {
await this.vfs.mkdir(dir, { recursive: true });
}
await this.vfs.writeFile(filePath, content);
}
async commit() {
// No-op for V1 as files are written immediately
}
join(...paths) {
return this.vfs.join(...paths);
}
directoryExists(path) {
return this.vfs.exists(path);
}
fileExists(path) {
return this.vfs.exists(path);
}
async getSourceFilesInDirectory(path) {
return (await this.vfs.readdir(path)).filter((file) => file.endsWith('.ts'));
}
}
//# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/project.ts"],"names":[],"mappings":"AAAA;;GAEG;AA4BH;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IAEjB;IACA;IAFV,YACU,GAAQ,EACR,MAAe;QADf,QAAG,GAAH,GAAG,CAAK;QACR,WAAM,GAAN,MAAM,CAAS;IACtB,CAAC;IAEJ,KAAK,CAAC,UAAU,CAAC,WAA2B,EAAE,QAAgB,EAAE,OAAe;QAC7E,qFAAqF;QACrF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACvC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,MAAM;QACV,gDAAgD;IAClD,CAAC;IAED,IAAI,CAAC,GAAG,KAAe;QACrB,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,eAAe,CAAC,IAAY;QAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,yBAAyB,CAAC,IAAY;QAC1C,OAAO,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/E,CAAC;CACF"}
@@ -0,0 +1,22 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import type { JsonObject } from '../../Common/jsonTypes.js';
/**
* Type guard for plain JSON objects. Used widely for safe property access
* on `unknown` schema values before codegen processing begins.
*/
export declare function isJsonObject(value: unknown): value is JsonObject;
/** Retrieves the properties map from a schema definition for iteration during code generation. */
export declare function getPropertiesNode(definition: JsonObject): JsonObject;
/** Returns the set of required property names from a schema, used to determine optionality in generated types. */
export declare function getRequiredFields(definition: JsonObject): Set<string>;
/** Converts a JSON Schema type to the TypeScript type used in generated interfaces. */
export declare function mapJsonTypeToTypeScript(jsonType?: string): string;
/** Resolves a JSON Schema `$ref` to the TypeScript type name it refers to. */
export declare function extractReferencedType(refValue: string): string;
/** Returns the schema's `name` field, which identifies the data source or connector. */
export declare function getNameNode(schema: unknown): string;
/** Recursively collects all `.json` file paths under the given directory. */
export declare function getAllJsonFiles(folderPath: string): Promise<string[]>;
//# sourceMappingURL=schemaUtils.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"schemaUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/schemaUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIzD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAGhE;AAED,kGAAkG;AAClG,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAKpE;AAED,kHAAkH;AAClH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,CAKrE;AAED,uFAAuF;AACvF,wBAAgB,uBAAuB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAcjE;AAED,8EAA8E;AAC9E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CASnD;AAED,6EAA6E;AAC7E,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA6B3E"}
@@ -0,0 +1,90 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import { getVfs } from '../../VfsManager.js';
import { convertToValidIdentifier, normalizeGenericTypeName } from './nameUtils.js';
/**
* Type guard for plain JSON objects. Used widely for safe property access
* on `unknown` schema values before codegen processing begins.
*/
export function isJsonObject(value) {
// Conclusive because 'object' type from JSON.parse() cannot have Symbol keys, or be undefined, functions, Dates, RegExp, Map/Set, or Classes
return value !== null && typeof value === 'object' && !Array.isArray(value);
}
/** Retrieves the properties map from a schema definition for iteration during code generation. */
export function getPropertiesNode(definition) {
if (!('properties' in definition) || !isJsonObject(definition.properties)) {
throw new Error("The definition does not contain a valid 'properties' node.");
}
return definition.properties;
}
/** Returns the set of required property names from a schema, used to determine optionality in generated types. */
export function getRequiredFields(definition) {
if ('required' in definition && Array.isArray(definition.required)) {
return new Set(definition.required.map((r) => String(r)));
}
return new Set();
}
/** Converts a JSON Schema type to the TypeScript type used in generated interfaces. */
export function mapJsonTypeToTypeScript(jsonType) {
switch (jsonType) {
case 'string':
return 'string';
case 'number':
case 'integer':
return 'number';
case 'boolean':
return 'boolean';
case 'object':
return 'Record<string, unknown>';
default:
return 'unknown';
}
}
/** Resolves a JSON Schema `$ref` to the TypeScript type name it refers to. */
export function extractReferencedType(refValue) {
const parts = refValue.split('/');
const raw = normalizeGenericTypeName(parts[parts.length - 1] ?? '');
return convertToValidIdentifier(raw);
}
/** Returns the schema's `name` field, which identifies the data source or connector. */
export function getNameNode(schema) {
if (isJsonObject(schema)) {
const name = schema.name;
if (typeof name !== 'string' || !name.trim()) {
throw new Error("No 'name' node found in schema");
}
return name.trim();
}
throw new Error('Schema must be a JSON object to extract name');
}
/** Recursively collects all `.json` file paths under the given directory. */
export async function getAllJsonFiles(folderPath) {
const files = [];
const vfs = getVfs();
async function recurse(dir) {
try {
const entries = await vfs.readdir(dir);
for (const entry of entries) {
const fullPath = vfs.join(dir, entry);
if (entry.endsWith('.json')) {
files.push(fullPath);
}
else {
// Check if it's a directory and recurse into it
const isDir = await vfs.isDirectory(fullPath);
if (isDir) {
await recurse(fullPath);
}
}
}
}
catch (error) {
// If we can't read the directory, skip it silently
// This could happen if it's not a directory or if permissions are insufficient
}
}
await recurse(folderPath);
return files;
}
//# sourceMappingURL=schemaUtils.js.map
@@ -0,0 +1 @@
{"version":3,"file":"schemaUtils.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/schemaUtils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
/** Returns true if the schema comes from a SharePoint data source. */
export declare function isSharepointSchema(root: unknown): boolean;
//# sourceMappingURL=sharepointUtils.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"sharepointUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/sharepointUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAMzD"}
@@ -0,0 +1,11 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import { isJsonObject } from './schemaUtils.js';
/** Returns true if the schema comes from a SharePoint data source. */
export function isSharepointSchema(root) {
// Logic to identify SharePoint schemas
// Check if the schema has SharePoint-specific properties or structure
return (isJsonObject(root) && !!root.referencedEntities && typeof root.referencedEntities === 'object');
}
//# sourceMappingURL=sharepointUtils.js.map
@@ -0,0 +1 @@
{"version":3,"file":"sharepointUtils.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/sharepointUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,sEAAsE;AACtE,MAAM,UAAU,kBAAkB,CAAC,IAAa;IAC9C,uCAAuC;IACvC,sEAAsE;IACtE,OAAO,CACL,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,kBAAkB,IAAI,OAAO,IAAI,CAAC,kBAAkB,KAAK,QAAQ,CAC/F,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import type { ReadOnlyVfs } from '../../Types/Vfs.types.js';
/** Returns true if the schema represents a SQL stored procedure rather than a table. */
export declare function isSqlStoredProcedure(root: unknown): boolean;
/**
* Returns the API key used to store a stored procedure in datasourcesinfo.
* Strips brackets and the schema prefix (e.g. `[dbo].[testProc]` → `testProc`).
* Must stay in sync with the equivalent logic in dataSourceInfoProcessor.ts.
*/
export declare function getSqlStoredProcApiKey(name: string): string;
/** Returns the name of the folder containing a schema file, used as the data source name for SQL stored procedures. */
export declare function getParentFolderName(schemaPath: string, vfs: ReadOnlyVfs): string;
//# sourceMappingURL=sqlUtils.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"sqlUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/sqlUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,wFAAwF;AACxF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAI3D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,uHAAuH;AACvH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,MAAM,CAUhF"}
@@ -0,0 +1,32 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import { isJsonObject } from './schemaUtils.js';
/** Returns true if the schema represents a SQL stored procedure rather than a table. */
export function isSqlStoredProcedure(root) {
// Logic to identify SQL stored procedures
// Check if the "schema" node contains a "procedureresultschema" node
return isJsonObject(root) && isJsonObject(root.schema) && !!root.schema.procedureresultschema;
}
/**
* Returns the API key used to store a stored procedure in datasourcesinfo.
* Strips brackets and the schema prefix (e.g. `[dbo].[testProc]` → `testProc`).
* Must stay in sync with the equivalent logic in dataSourceInfoProcessor.ts.
*/
export function getSqlStoredProcApiKey(name) {
const withoutBrackets = name.replace(/\[|\]/g, '');
return ('/' + withoutBrackets).replace(/^\/.+\./, '');
}
/** Returns the name of the folder containing a schema file, used as the data source name for SQL stored procedures. */
export function getParentFolderName(schemaPath, vfs) {
const directoryName = vfs.dirname(schemaPath);
if (!directoryName) {
throw new Error('The schemaPath does not have a valid directory.');
}
const parentFolderName = vfs.basename(directoryName);
if (!parentFolderName) {
throw new Error('The directory does not have a valid parent folder name.');
}
return parentFolderName.toLowerCase();
}
//# sourceMappingURL=sqlUtils.js.map
@@ -0,0 +1 @@
{"version":3,"file":"sqlUtils.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/sqlUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,wFAAwF;AACxF,MAAM,UAAU,oBAAoB,CAAC,IAAa;IAChD,0CAA0C;IAC1C,qEAAqE;IACrE,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC;AAChG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,GAAG,eAAe,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,uHAAuH;AACvH,MAAM,UAAU,mBAAmB,CAAC,UAAkB,EAAE,GAAgB;IACtE,MAAM,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC9C,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,gBAAgB,GAAG,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACrD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,gBAAgB,CAAC,WAAW,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,47 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import type { ILogger } from '../../services/index.js';
import type { ReadOnlyVfs } from '../../Types/Vfs.types.js';
import type { CodegenV2Project } from '../v2/project.js';
import type { ICodegenProject } from './project.js';
interface ISharedContext {
/** The folder containing the schema file being processed */
schemaFolder: string;
/** The path to the specific schema file being processed */
schemaFile: string;
/** The output folder where generated TypeScript files will be written */
outputFolder: string;
/** Logger for diagnostic messages */
logger: ILogger;
}
/**
* Vfs needed to read provided schema files.
*/
export interface IProcessingContext extends ISharedContext {
/** Virtual file system for (only) reading schema files */
vfs: ReadOnlyVfs;
}
/**
* Contains additional context as a result of parsing schema.
* Vfs is replaced by ICodegenProject for all IO.
*/
export interface ICodegenContext extends ISharedContext {
/** Parsed JSON schema node */
schema: unknown;
/** The model name extracted from the schema (e.g., 'Account', 'Contact') */
modelName: string;
/** The data source name (e.g., 'Dataverse', 'Sharepoint', 'SqlServer') */
dataSourceName: string;
/** Codegen project for managing files */
project: ICodegenProject;
/** is V2 codegen enabled */
isV2Enabled: boolean;
}
export interface ICodegenV2Context extends ICodegenContext {
project: CodegenV2Project;
isV2Enabled: true;
}
export declare function isCodegenV2Context(context: ICodegenContext): context is ICodegenV2Context;
export {};
//# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,UAAU,cAAc;IACtB,4DAA4D;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,0DAA0D;IAC1D,GAAG,EAAE,WAAW,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,8BAA8B;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,cAAc,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,OAAO,EAAE,eAAe,CAAC;IACzB,4BAA4B;IAC5B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,OAAO,EAAE,gBAAgB,CAAC;IAC1B,WAAW,EAAE,IAAI,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,IAAI,iBAAiB,CAEzF"}
@@ -0,0 +1,7 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
export function isCodegenV2Context(context) {
return context.isV2Enabled;
}
//# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgDH,MAAM,UAAU,kBAAkB,CAAC,OAAwB;IACzD,OAAO,OAAO,CAAC,WAAW,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,18 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import type { JsonObject } from '../../../Common/jsonTypes.js';
import type { ICodegenV2Context } from '../../shared/types.js';
interface GenerateModelContext {
parentContext: ICodegenV2Context;
schema: JsonObject;
fileColumnNames: string[];
imageColumnNames: string[];
modelFilePath: string;
}
export declare function generateDataverseModel({ parentContext, schema, fileColumnNames, imageColumnNames, modelFilePath, }: GenerateModelContext): Promise<{
hasLookupColumns: boolean;
multiSelectPicklistFields: string[];
}>;
export default generateDataverseModel;
//# sourceMappingURL=generateDataverseModel.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"generateDataverseModel.d.ts","sourceRoot":"","sources":["../../../../src/CodeGen/v2/Dataverse/generateDataverseModel.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAoB5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,UAAU,oBAAoB;IAC5B,aAAa,EAAE,iBAAiB,CAAC;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,aAAa,EACb,MAAM,EACN,eAAe,EACf,gBAAgB,EAChB,aAAa,GACd,EAAE,oBAAoB,GAAG,OAAO,CAAC;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,yBAAyB,EAAE,MAAM,EAAE,CAAC;CACrC,CAAC,CAsCD;AA6RD,eAAe,sBAAsB,CAAC"}
@@ -0,0 +1,227 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import { VariableDeclarationKind } from 'ts-morph';
import { formatEnumLabel, generateLookupValuePropertyName, generateODataBindPropertyName, generateOptionsetEnumName, getDataverseLookupColumns, getDataverseMultiSelectPicklistFields, hasOptionsetData, iterateDataverseProperties, } from '../../shared/dataverseUtils.js';
import { determineTypeScriptType } from '../../shared/modelUtils.js';
import { convertToValidIdentifier, formatPropertyName, normalizePropertyName, sanitizeName, } from '../../shared/nameUtils.js';
import { getPropertiesNode, getRequiredFields, isJsonObject } from '../../shared/schemaUtils.js';
export async function generateDataverseModel({ parentContext, schema, fileColumnNames, imageColumnNames, modelFilePath, }) {
const { modelName, project, logger } = parentContext;
const sourceFile = await project.createFile('model', modelFilePath, '');
const propertiesNode = getPropertiesNode(schema);
const lookupColumns = getDataverseLookupColumns(propertiesNode);
const hasLookupColumns = lookupColumns.size > 0;
const multiSelectPicklistFields = getDataverseMultiSelectPicklistFields(propertiesNode);
addOptionsetDeclarations(sourceFile, modelName, propertiesNode);
if (!hasLookupColumns) {
addFullInterface(sourceFile, modelName, schema, propertiesNode);
}
else {
const baseInterfaceName = `${modelName}Base`;
addBaseInterface(sourceFile, baseInterfaceName, modelName, schema, lookupColumns, propertiesNode, logger);
addExtendedInterface(sourceFile, modelName, baseInterfaceName, schema, lookupColumns, propertiesNode);
}
addUploadColumnNameType(sourceFile, modelName, fileColumnNames, imageColumnNames);
return { hasLookupColumns, multiSelectPicklistFields };
}
// ─── Upload Column Name Type ──────────────────────────────────────────────────
function addUploadColumnNameType(sourceFile, modelName, fileColumnNames, imageColumnNames) {
const id = convertToValidIdentifier(modelName);
if (fileColumnNames.length > 0) {
sourceFile.addTypeAlias({
name: `${id}FileColumnName`,
isExported: true,
type: fileColumnNames.map((col) => `'${col}'`).join(' | '),
});
}
if (imageColumnNames.length > 0) {
sourceFile.addTypeAlias({
name: `${id}ImageColumnName`,
isExported: true,
type: imageColumnNames.map((col) => `'${col}'`).join(' | '),
});
}
if (fileColumnNames.length === 0 && imageColumnNames.length === 0) {
return;
}
const uploadType = fileColumnNames.length > 0 && imageColumnNames.length > 0
? `${id}FileColumnName | ${id}ImageColumnName`
: fileColumnNames.length > 0
? `${id}FileColumnName`
: `${id}ImageColumnName`;
sourceFile.addTypeAlias({
name: `${id}UploadColumnName`,
isExported: true,
type: uploadType,
});
}
// ─── Optionset Declarations ───────────────────────────────────────────────────
function addOptionsetDeclarations(sourceFile, modelName, propertiesNode) {
for (const [propName, propValue] of Object.entries(propertiesNode)) {
if (!isJsonObject(propValue) || !hasOptionsetData(propValue)) {
continue;
}
const enumName = generateOptionsetEnumName(modelName, propName);
const enumValues = propValue['x-ms-enum-values'];
const enumLabels = propValue.enum;
if (!Array.isArray(enumValues)) {
// No x-ms-enum-values: emit a string union type alias from the enum labels.
sourceFile.addTypeAlias({
name: enumName,
isExported: true,
type: enumLabels.map(formatEnumLabel).join(' | '),
});
continue;
}
if (!enumValues.every((val) => typeof val === 'number')) {
throw new Error(`Unsupported Dataverse optionset format: x-ms-enum-values must be an array of numbers. Property: ${propName} in model ${modelName}`);
}
// Dataverse format: x-ms-enum-values contains numeric values.
// Emit a const object mapping numeric keys to display names, plus a keyof type alias.
const entries = enumLabels.map((label, i) => {
const sanitizedLabel = !label || (typeof label === 'string' && label.trim() === '')
? `EmptyOption${i}`
: sanitizeName(label);
return ` ${enumValues[i]}: '${sanitizedLabel}'`;
});
sourceFile.addVariableStatement({
declarationKind: VariableDeclarationKind.Const,
isExported: true,
declarations: [
{
name: enumName,
initializer: `{\n${entries.join(',\n')}\n} as const`,
},
],
});
sourceFile.addTypeAlias({
name: enumName,
isExported: true,
type: `keyof typeof ${enumName}`,
});
}
}
/**
* Adds a single interface for entities with no lookup columns.
* All properties (including read-only) are included.
*/
function addFullInterface(sourceFile, modelName, schema, //DataverseEntitySchema,
propertiesNode //DataverseEntitySchema['properties']
) {
const requiredFields = getRequiredFields(schema);
const properties = Object.entries(propertiesNode).map(([propName, propValue]) => {
const propValueAsObject = isJsonObject(propValue) ? propValue : undefined;
const formattedName = normalizePropertyName(formatPropertyName(convertToValidIdentifier(propName)));
const description = typeof propValueAsObject?.description === 'string'
? propValueAsObject.description
: undefined;
return {
name: formattedName,
type: determineTypeScriptType(modelName, propValueAsObject, '', propName),
hasQuestionToken: !requiredFields.has(propName),
docs: description ? [{ description }] : undefined,
};
});
sourceFile.addInterface({
name: convertToValidIdentifier(modelName),
isExported: true,
properties,
});
}
/**
* Adds the base interface for PATCH/POST operations on entities with lookup columns.
* - Skips read-only properties (they belong in the extended GET interface)
* - Replaces lookup columns with @odata.bind string properties
* - Skips lookup-related name properties (e.g., owneridname)
*/
function addBaseInterface(sourceFile, interfaceName, originalModelName, schema, lookupColumns, propertiesNode, logger) {
const properties = [];
const requiredFields = getRequiredFields(schema);
for (const prop of iterateDataverseProperties(propertiesNode, lookupColumns)) {
const { type, readOnly, propName, propValue } = prop;
// lookupRelated properties (e.g., owneridname) should always be read-only.
// Log a warning if an anomaly is detected, but it will be skipped regardless.
if (logger && type === 'lookupRelated' && !readOnly) {
logger.trackActivityEvent('CodegenV2.generateDataverseModel.addBaseInterface.warning', {
message: 'Anomaly: lookupRelated property is not read-only',
modelName: originalModelName,
columnName: propName,
});
}
// Skip read-only properties and lookup-related properties (e.g., owneridname)
// They are handled in the extended interface, and cannot be written to.
if (readOnly || type === 'lookupRelated') {
continue;
}
// Replace the lookup columns with an @odata.bind property for write operations.
if (type === 'lookup') {
properties.push({
name: formatPropertyName(generateODataBindPropertyName(prop.schemaName)),
type: 'string',
hasQuestionToken: !requiredFields.has(propName),
});
continue;
}
const formattedName = normalizePropertyName(formatPropertyName(convertToValidIdentifier(propName)));
const description = typeof propValue.description === 'string' ? propValue.description : undefined;
properties.push({
name: formattedName,
type: determineTypeScriptType(originalModelName, propValue, '', propName),
hasQuestionToken: !requiredFields.has(propName),
docs: description ? [{ description }] : undefined,
});
}
sourceFile.addInterface({
name: convertToValidIdentifier(interfaceName),
isExported: true,
properties,
});
}
/**
* Adds the extended interface for GET operations on entities with lookup columns.
* Extends the base interface and adds:
* - Read-only properties (excluding lookup columns, which are handled separately)
* - Lookup object properties (e.g., ownerid?: object)
* - Lookup value properties (e.g., _ownerid_value?: string)
*/
function addExtendedInterface(sourceFile, modelName, baseInterfaceName, schema, lookupColumns, propertiesNode) {
const requiredFields = getRequiredFields(schema);
const properties = [];
for (const { type, readOnly, propName, propValue } of iterateDataverseProperties(propertiesNode, lookupColumns)) {
// Exclude non-readonly properties (they are handled in the base interface)
// Exclude lookup properties, they are handled separately below
if (!readOnly || type === 'lookup') {
continue;
}
const formattedName = normalizePropertyName(formatPropertyName(convertToValidIdentifier(propName)));
const description = typeof propValue.description === 'string' ? propValue.description : undefined;
properties.push({
name: formattedName,
type: determineTypeScriptType(modelName, propValue, '', propName),
hasQuestionToken: !requiredFields.has(propName),
docs: description ? [{ description }] : undefined,
});
}
// Lookup-specific properties: the lookup object and its extracted GUID value.
for (const lookupPropName of lookupColumns.keys()) {
properties.push({
name: formatPropertyName(convertToValidIdentifier(lookupPropName)),
type: 'object',
hasQuestionToken: true,
});
properties.push({
name: formatPropertyName(generateLookupValuePropertyName(lookupPropName)),
type: 'string',
hasQuestionToken: true,
});
}
sourceFile.addInterface({
name: convertToValidIdentifier(modelName),
isExported: true,
extends: [convertToValidIdentifier(baseInterfaceName)],
properties,
});
}
export default generateDataverseModel;
//# sourceMappingURL=generateDataverseModel.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,17 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import type { ICodegenV2Context } from '../../shared/types.js';
interface GenerateServiceContext {
parentContext: ICodegenV2Context;
primaryKey: string;
hasLookupColumns: boolean;
multiSelectPicklistFields: string[];
fileColumnNames: string[];
imageColumnNames: string[];
serviceFilePath: string;
dataSourceInfoPath: string;
}
export declare function generateDataverseService({ parentContext, primaryKey, hasLookupColumns, multiSelectPicklistFields, fileColumnNames, imageColumnNames, serviceFilePath, dataSourceInfoPath, }: GenerateServiceContext): Promise<void>;
export default generateDataverseService;
//# sourceMappingURL=generateDataverseService.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"generateDataverseService.d.ts","sourceRoot":"","sources":["../../../../src/CodeGen/v2/Dataverse/generateDataverseService.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,UAAU,sBAAsB;IAC9B,aAAa,EAAE,iBAAiB,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,yBAAyB,EAAE,MAAM,EAAE,CAAC;IACpC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,wBAAwB,CAAC,EAC7C,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,yBAAyB,EACzB,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,kBAAkB,GACnB,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyUxC;AAED,eAAe,wBAAwB,CAAC"}
@@ -0,0 +1,323 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import { Scope } from 'ts-morph';
import { convertToValidIdentifier } from '../../shared/nameUtils.js';
export async function generateDataverseService({ parentContext, primaryKey, hasLookupColumns, multiSelectPicklistFields, fileColumnNames, imageColumnNames, serviceFilePath, dataSourceInfoPath, }) {
const { modelName, dataSourceName, project } = parentContext;
const serviceName = `${convertToValidIdentifier(modelName)}Service`;
// When there are lookup columns the model generator emits a separate ModelNameBase interface
// for write operations (create/update) and ModelName extends it for read operations.
// When there are none, a single ModelName interface covers all operations.
const writeTypeName = hasLookupColumns
? `${convertToValidIdentifier(modelName)}Base`
: convertToValidIdentifier(modelName);
const readTypeName = convertToValidIdentifier(modelName);
const id = convertToValidIdentifier(modelName);
const fileColumnNameType = `${id}FileColumnName`;
const imageColumnNameType = `${id}ImageColumnName`;
const uploadColumnNameType = `${id}UploadColumnName`;
const hasFileColumns = fileColumnNames.length > 0;
const hasImageColumns = imageColumnNames.length > 0;
const hasFileOrImageColumns = hasFileColumns || hasImageColumns;
const hasMultiSelectFields = multiSelectPicklistFields.length > 0;
const createParamType = primaryKey !== '' ? `Omit<${writeTypeName}, '${primaryKey}'>` : writeTypeName;
const updateParamType = primaryKey !== '' ? `Partial<Omit<${writeTypeName}, '${primaryKey}'>>` : writeTypeName;
const sourceFile = await project.createFile('service', serviceFilePath, '');
// External package imports — fixMissingImports cannot resolve these.
const dataValueImports = ['getClient'];
if (hasMultiSelectFields) {
dataValueImports.push('deserializeMultiSelectPicklistFields', 'serializeMultiSelectPicklistFields');
}
sourceFile.addImportDeclaration({
moduleSpecifier: '@microsoft/power-apps/data',
namedImports: dataValueImports,
});
sourceFile.addImportDeclaration({
moduleSpecifier: '@microsoft/power-apps/data',
namedImports: ['IOperationResult'],
isTypeOnly: true,
});
sourceFile.addImportDeclaration({
moduleSpecifier: '@microsoft/power-apps/data/metadata/dataverse',
namedImports: ['EntityMetadata', 'GetEntityMetadataOptions'],
isTypeOnly: true,
});
sourceFile.addImportDeclaration({
moduleSpecifier: dataSourceInfoPath,
namedImports: ['dataSourcesInfo'],
});
// Internal in-project type imports - fixMissingImports would auto-add these at commit time,
// but it cannot detect that these are type-only imports. We add them explicitly to make them type-only.
sourceFile.addImportDeclaration({
moduleSpecifier: sourceFile.getRelativePathAsModuleSpecifierTo(project.getSourceFile('commonModel')),
namedImports: ['IGetAllOptions', 'IGetOptions'],
isTypeOnly: true,
});
const modelNamedImports = hasLookupColumns ? [writeTypeName, readTypeName] : [readTypeName];
if (hasFileColumns) {
modelNamedImports.push(fileColumnNameType);
}
if (hasImageColumns) {
modelNamedImports.push(imageColumnNameType);
}
if (hasFileOrImageColumns) {
modelNamedImports.push(uploadColumnNameType);
}
sourceFile.addImportDeclaration({
moduleSpecifier: sourceFile.getRelativePathAsModuleSpecifierTo(project.getSourceFile('model')),
namedImports: modelNamedImports,
isTypeOnly: true,
});
const multiSelectFieldsInitializer = `[${multiSelectPicklistFields.map((f) => `'${f}'`).join(', ')}] as const`;
sourceFile.addClass({
name: serviceName,
isExported: true,
properties: [
{
name: 'dataSourceName',
isStatic: true,
isReadonly: true,
scope: Scope.Private,
initializer: `'${dataSourceName.toLowerCase()}'`,
},
{
name: 'client',
isStatic: true,
isReadonly: true,
scope: Scope.Private,
initializer: 'getClient(dataSourcesInfo)',
},
// Emitted only when the entity has MultiSelectPicklist columns. Used as the field list
// passed to serializeMultiSelectPicklistFields / deserializeMultiSelectPicklistFields.
...(hasMultiSelectFields
? [
{
name: 'multiSelectPicklistFields',
isStatic: true,
isReadonly: true,
scope: Scope.Private,
initializer: multiSelectFieldsInitializer,
},
]
: []),
],
methods: [
{
name: 'create',
isStatic: true,
isAsync: true,
scope: Scope.Public,
parameters: [{ name: 'record', type: createParamType }],
returnType: `Promise<IOperationResult<${readTypeName}>>`,
statements: hasMultiSelectFields
? [
`const result = await ${serviceName}.client.createRecordAsync<Record<string, unknown>, ${readTypeName}>(${serviceName}.dataSourceName, serializeMultiSelectPicklistFields(record as Record<string, unknown>, ${serviceName}.multiSelectPicklistFields));`,
`if (result.data) { deserializeMultiSelectPicklistFields(result.data as Record<string, unknown>, ${serviceName}.multiSelectPicklistFields); }`,
'return result;',
]
: [
`const result = await ${serviceName}.client.createRecordAsync<${createParamType}, ${readTypeName}>(${serviceName}.dataSourceName, record);`,
'return result;',
],
},
{
name: 'update',
isStatic: true,
isAsync: true,
scope: Scope.Public,
parameters: [
{ name: 'id', type: 'string' },
{ name: 'changedFields', type: updateParamType },
],
returnType: `Promise<IOperationResult<${readTypeName}>>`,
statements: hasMultiSelectFields
? [
`const result = await ${serviceName}.client.updateRecordAsync<Record<string, unknown>, ${readTypeName}>(${serviceName}.dataSourceName, id, serializeMultiSelectPicklistFields(changedFields as Record<string, unknown>, ${serviceName}.multiSelectPicklistFields));`,
`if (result.data) { deserializeMultiSelectPicklistFields(result.data as Record<string, unknown>, ${serviceName}.multiSelectPicklistFields); }`,
'return result;',
]
: [
`const result = await ${serviceName}.client.updateRecordAsync<${updateParamType}, ${readTypeName}>(${serviceName}.dataSourceName, id, changedFields);`,
'return result;',
],
},
{
name: 'delete',
isStatic: true,
isAsync: true,
scope: Scope.Public,
parameters: [{ name: 'id', type: 'string' }],
returnType: 'Promise<void>',
statements: [
`await ${serviceName}.client.deleteRecordAsync(${serviceName}.dataSourceName, id);`,
],
},
{
name: 'get',
isStatic: true,
isAsync: true,
scope: Scope.Public,
parameters: [
{ name: 'id', type: 'string' },
{ name: 'options', type: 'IGetOptions', hasQuestionToken: true },
],
returnType: `Promise<IOperationResult<${readTypeName}>>`,
statements: hasMultiSelectFields
? [
`const result = await ${serviceName}.client.retrieveRecordAsync<${readTypeName}>(${serviceName}.dataSourceName, id, options);`,
`if (result.data) { deserializeMultiSelectPicklistFields(result.data as Record<string, unknown>, ${serviceName}.multiSelectPicklistFields); }`,
'return result;',
]
: [
`const result = await ${serviceName}.client.retrieveRecordAsync<${readTypeName}>(${serviceName}.dataSourceName, id, options);`,
'return result;',
],
},
{
name: 'getAll',
isStatic: true,
isAsync: true,
scope: Scope.Public,
parameters: [{ name: 'options', type: 'IGetAllOptions', hasQuestionToken: true }],
returnType: `Promise<IOperationResult<${readTypeName}[]>>`,
statements: hasMultiSelectFields
? [
`const result = await ${serviceName}.client.retrieveMultipleRecordsAsync<${readTypeName}>(${serviceName}.dataSourceName, options);`,
`result.data?.forEach(record => deserializeMultiSelectPicklistFields(record as Record<string, unknown>, ${serviceName}.multiSelectPicklistFields));`,
'return result;',
]
: [
`const result = await ${serviceName}.client.retrieveMultipleRecordsAsync<${readTypeName}>(${serviceName}.dataSourceName, options);`,
'return result;',
],
},
{
name: 'getMetadata',
isStatic: true,
scope: Scope.Public,
parameters: [
{
name: 'options',
type: `GetEntityMetadataOptions<${readTypeName}>`,
initializer: '{}',
},
],
returnType: `Promise<IOperationResult<Partial<EntityMetadata>>>`,
statements: [
`return ${serviceName}.client.executeAsync({`,
` dataverseRequest: {`,
` action: 'getEntityMetadata',`,
` parameters: {`,
` tableName: ${serviceName}.dataSourceName,`,
` options: options as GetEntityMetadataOptions,`,
` },`,
` },`,
`});`,
],
},
...(hasFileOrImageColumns
? [
{
name: 'upload',
isStatic: true,
isAsync: true,
scope: Scope.Public,
parameters: [
{ name: 'id', type: 'string' },
{ name: 'columnName', type: uploadColumnNameType },
{ name: 'file', type: 'File' },
{ name: 'fileDisplayName', type: 'string', hasQuestionToken: true },
],
returnType: 'Promise<IOperationResult<void>>',
statements: [
'const arrayBuffer = await file.arrayBuffer();',
'const data = new Uint8Array(arrayBuffer);',
`const result = await ${serviceName}.client.uploadFileToRecord(`,
` ${serviceName}.dataSourceName,`,
` id,`,
` columnName,`,
` fileDisplayName || file.name,`,
` data,`,
`);`,
'return result;',
],
},
]
: []),
...(hasFileColumns
? [
{
name: 'downloadFile',
isStatic: true,
isAsync: true,
scope: Scope.Public,
parameters: [
{ name: 'id', type: 'string' },
{ name: 'columnName', type: fileColumnNameType },
],
returnType: 'Promise<IOperationResult<Uint8Array>>',
statements: [
`const result = await ${serviceName}.client.downloadFileFromRecord(`,
` ${serviceName}.dataSourceName,`,
` id,`,
` columnName,`,
`);`,
'return result;',
],
},
]
: []),
...(hasImageColumns
? [
{
name: 'downloadImage',
isStatic: true,
isAsync: true,
scope: Scope.Public,
parameters: [
{ name: 'id', type: 'string' },
{ name: 'columnName', type: imageColumnNameType },
{ name: 'fullSize', type: 'boolean', initializer: 'false' },
],
returnType: 'Promise<IOperationResult<Uint8Array>>',
statements: [
`const result = await ${serviceName}.client.downloadImageFromRecord(`,
` ${serviceName}.dataSourceName,`,
` id,`,
` columnName,`,
` fullSize,`,
`);`,
'return result;',
],
},
]
: []),
...(hasFileOrImageColumns
? [
{
name: 'deleteFileOrImage',
isStatic: true,
isAsync: true,
scope: Scope.Public,
parameters: [
{ name: 'id', type: 'string' },
{ name: 'columnName', type: uploadColumnNameType },
],
returnType: 'Promise<IOperationResult<void>>',
statements: [
`const result = await ${serviceName}.client.deleteFileOrImageFromRecord(`,
` ${serviceName}.dataSourceName,`,
` id,`,
` columnName,`,
`);`,
'return result;',
],
},
]
: []),
],
});
}
export default generateDataverseService;
//# sourceMappingURL=generateDataverseService.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import type prettier from 'prettier';
export declare const defaultPrettierConfig: prettier.Options;
//# sourceMappingURL=defaultPrettierConfig.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"defaultPrettierConfig.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/v2/defaultPrettierConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAErC,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,OAK5C,CAAC"}
@@ -0,0 +1,10 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
export const defaultPrettierConfig = {
endOfLine: 'auto',
printWidth: 100,
singleQuote: true,
trailingComma: 'es5',
};
//# sourceMappingURL=defaultPrettierConfig.js.map
@@ -0,0 +1 @@
{"version":3,"file":"defaultPrettierConfig.js","sourceRoot":"","sources":["../../../src/CodeGen/v2/defaultPrettierConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,CAAC,MAAM,qBAAqB,GAAqB;IACrD,SAAS,EAAE,MAAM;IACjB,UAAU,EAAE,GAAG;IACf,WAAW,EAAE,IAAI;IACjB,aAAa,EAAE,KAAK;CACrB,CAAC"}
@@ -0,0 +1,26 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import { type CompilerOptions, type SourceFile } from 'ts-morph';
import type { ILogger, Scenario } from '../../services/index.js';
import type { Vfs } from '../../Types/Vfs.types.js';
import type { FileIdentifier, ICodegenProject } from '../shared/project.js';
export declare class CodegenV2Project implements ICodegenProject {
private vfs;
private logger;
private tsMorphProject;
private fileMap;
constructor(vfs: Vfs, logger: ILogger, compilerOptions?: CompilerOptions);
createFile(identifier: FileIdentifier, filePath: string, content: string): Promise<SourceFile>;
commit(): Promise<void>;
join(...paths: string[]): string;
fileExists(path: string): Promise<boolean>;
directoryExists(path: string): Promise<boolean>;
getSourceFilesInDirectory(path: string): Promise<string[]>;
getSourceFile(identifier: FileIdentifier): SourceFile;
normalizeFile(sourceFile: SourceFile, scenario?: Scenario): Promise<{
content: string;
path: string;
}>;
}
//# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/v2/project.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,KAAK,eAAe,EAAW,KAAK,UAAU,EAAM,MAAM,UAAU,CAAC;AAE9E,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAqBzE,qBAAa,gBAAiB,YAAW,eAAe;IAKpD,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,MAAM;IALhB,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,OAAO,CAAkC;gBAGvC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,OAAO,EACvB,eAAe,CAAC,EAAE,eAAe;IAiB7B,UAAU,CACd,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,CAAC;IAahB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC7B,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM;IAI1B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1C,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI/C,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQhE,aAAa,CAAC,UAAU,EAAE,cAAc,GAAG,UAAU;IAU/C,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,QAAQ,CAAC,EAAE,QAAQ,GAClB,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAwC9C"}
@@ -0,0 +1,145 @@
/*!
* Copyright (C) Microsoft Corporation. All rights reserved.
*/
import prettier from 'prettier';
import { Project, ts } from 'ts-morph';
import { defaultPrettierConfig } from './defaultPrettierConfig.js';
const defaultCompilerOptions = {
target: ts.ScriptTarget.ES2020,
module: ts.ModuleKind.ESNext,
};
const COPYRIGHT_BANNER = [
'/*!',
' * Copyright (C) Microsoft Corporation. All rights reserved.',
' * This file is autogenerated. Do not edit this file directly.',
' */',
'',
'',
].join('\n');
/*
Compatibility layer for V1 and V2 codegen projects. The purpose of this class is
purely to allow V1 modelServiceGenerator to use ts-morph backend without modification.
*/
export class CodegenV2Project {
vfs;
logger;
tsMorphProject;
fileMap;
constructor(vfs, logger, compilerOptions) {
this.vfs = vfs;
this.logger = logger;
const _compilerOptions = compilerOptions ?? defaultCompilerOptions;
this.tsMorphProject = new Project({
useInMemoryFileSystem: true,
compilerOptions: _compilerOptions,
});
this.logger?.trackActivityEvent('Codegenv2.project.constructor', {
message: 'Project created',
compilerOptions: _compilerOptions,
});
this.fileMap = new Map();
}
async createFile(identifier, filePath, content) {
const sourceFile = this.tsMorphProject.createSourceFile(filePath, content, { overwrite: true });
if (this.fileMap.has(identifier)) {
this.logger?.trackActivityEvent('Codegenv2.project.createFile.duplicateIdentifier', {
message: `File with identifier ${identifier} already exists. Overwriting previous file.`,
identifier,
file: sourceFile.getBaseName(),
});
}
this.fileMap.set(identifier, sourceFile);
return sourceFile;
}
async commit() {
const scenario = this.logger?.trackScenario('Codegenv2.project.commit');
const normalizedContent = await Promise.all(this.tsMorphProject
.getSourceFiles()
.map((sourceFile) => this.normalizeFile(sourceFile, scenario)));
// Write all to Vfs (stops on first error)
let writtenCount = 0;
try {
for (const { path, content } of normalizedContent) {
await this.vfs.writeFile(path, content);
writtenCount++;
}
}
catch (error) {
scenario?.failure({
message: 'Failed to commit all files to Vfs',
error: this.logger?.stringifyError(error),
filesWritten: writtenCount,
totalFiles: normalizedContent.length,
failedFile: normalizedContent[writtenCount]?.path,
});
throw error;
}
scenario?.complete({
message: 'Successfully committed files to Vfs',
filesWritten: writtenCount,
totalFiles: normalizedContent.length,
});
}
join(...paths) {
return this.vfs.join(...paths);
}
async fileExists(path) {
return !!this.tsMorphProject.getSourceFile(path);
}
async directoryExists(path) {
return !!this.tsMorphProject.getDirectory(path);
}
async getSourceFilesInDirectory(path) {
const directory = this.tsMorphProject.getDirectory(path);
if (!directory) {
return [];
}
return directory.getSourceFiles().map((file) => file.getBaseName());
}
getSourceFile(identifier) {
const result = this.fileMap.get(identifier);
if (!result) {
throw new Error(`File with identifier ${identifier} not found. Possibly fetched before creation.`);
}
return result;
}
async normalizeFile(sourceFile, scenario) {
const rawContent = sourceFile.getFullText();
const path = sourceFile.getFilePath();
// 1. Insert copyright banner if not present
if (!rawContent.includes(COPYRIGHT_BANNER.trim())) {
sourceFile.insertText(0, COPYRIGHT_BANNER);
}
// 2. Fix missing imports and organize them
try {
sourceFile.fixMissingImports();
sourceFile.organizeImports();
}
catch (error) {
scenario?.failure({
message: 'Failed to organize and fix missing imports',
file: sourceFile.getBaseName(),
});
throw error;
}
// 3. Run prettier
let content;
try {
content = await prettier.format(sourceFile.getFullText(), {
...defaultPrettierConfig,
parser: 'typescript',
});
}
catch (error) {
scenario?.failure({
message: 'Failed to format files with Prettier',
file: sourceFile.getBaseName(),
});
const e = error;
console.error('Prettier formatting error:', e.message, e.codeFrame ?? '');
throw error;
}
return { content, path };
}
}
//# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../../src/CodeGen/v2/project.ts"],"names":[],"mappings":"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"}