Skip to content

Parsed Action Schema #841

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions ts/packages/actionSchema/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export {
ActionSchemaTypeDefinition,
ActionSchemaEntryTypeDefinition,
ActionSchemaGroup,
ActionSchemaFile,
ParsedActionSchema,
ActionSchemaObject,
ActionSchemaUnion,
} from "./type.js";
Expand All @@ -31,11 +31,9 @@ export { getParameterType, getParameterNames } from "./utils.js";
export * as ActionSchemaCreator from "./creator.js";

export {
ActionSchemaFileJSON,
toJSONActionSchemaFile,
fromJSONActionSchemaFile,
loadParsedActionSchema,
saveParsedActionSchema,
ParsedActionSchemaJSON,
toJSONParsedActionSchema,
fromJSONParsedActionSchema,
} from "./serialize.js";

// Generic (non-action) Schema
Expand Down
25 changes: 9 additions & 16 deletions ts/packages/actionSchema/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import {
SchemaTypeUnion,
SchemaTypeDefinition,
ActionSchemaTypeDefinition,
ActionSchemaFile,
ActionSchemaEntryTypeDefinition,
SchemaObjectField,
ParsedActionSchema,
} from "./type.js";
import ts from "typescript";
import { ActionParamSpecs, SchemaConfig } from "./schemaConfig.js";
Expand Down Expand Up @@ -173,14 +173,13 @@ function checkActionSchema(
return [actionNameString, actionDefinition];
}

export function createActionSchemaFile(
export function createParsedActionSchema(
schemaName: string,
sourceHash: string,
entry: SchemaTypeDefinition,
order: Map<string, number> | undefined,
strict: boolean,
schemaConfig?: SchemaConfig,
): ActionSchemaFile {
): ParsedActionSchema {
if (strict && !entry.exported) {
throw new Error(
`Schema Error: ${schemaName}: Type '${entry.name}' must be exported`,
Expand Down Expand Up @@ -248,30 +247,27 @@ export function createActionSchemaFile(
if (actionSchemas.size === 0) {
throw new Error("No action schema found");
}
const actionSchemaFile: ActionSchemaFile = {
const parsedActionSchema: ParsedActionSchema = {
entry: entry as ActionSchemaEntryTypeDefinition,
sourceHash,
schemaName,
actionSchemas,
};
if (schemaConfig?.actionNamespace === true) {
actionSchemaFile.actionNamespace = true;
parsedActionSchema.actionNamespace = true;
}
if (order) {
actionSchemaFile.order = order;
parsedActionSchema.order = order;
}
return actionSchemaFile;
return parsedActionSchema;
}

export function parseActionSchemaSource(
source: string,
schemaName: string,
sourceHash: string,
typeName: string,
fileName: string = "",
schemaConfig?: SchemaConfig,
strict: boolean = false,
): ActionSchemaFile {
): ParsedActionSchema {
debug(`Parsing ${schemaName} for ${typeName}: ${fileName}`);
try {
const sourceFile = ts.createSourceFile(
Expand All @@ -282,7 +278,6 @@ export function parseActionSchemaSource(
return ActionParser.parseSourceFile(
sourceFile,
schemaName,
sourceHash,
typeName,
schemaConfig,
strict,
Expand All @@ -296,7 +291,6 @@ class ActionParser {
static parseSourceFile(
sourceFile: ts.SourceFile,
schemaName: string,
sourceHash: string,
typeName: string,
schemaConfig: SchemaConfig | undefined,
strict: boolean,
Expand All @@ -306,9 +300,8 @@ class ActionParser {
if (definition === undefined) {
throw new Error(`Type '${typeName}' not found`);
}
const result = createActionSchemaFile(
const result = createParsedActionSchema(
schemaName,
sourceHash,
definition,
parser.typeOrder,
strict,
Expand Down
82 changes: 20 additions & 62 deletions ts/packages/actionSchema/src/serialize.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { createActionSchemaFile } from "./parser.js";
import { createParsedActionSchema } from "./parser.js";
import {
ActionSchemaFile,
ParsedActionSchema,
SchemaType,
SchemaTypeDefinition,
} from "./type.js";

export type ParsedActionSchemaGroupJSON = {
export type ParsedActionSchemaJSON = {
entry: string;
types: Record<string, SchemaTypeDefinition>;
actionNamespace?: boolean; // default to false
order?: Record<string, number>;
};

export type ActionSchemaFileJSON = {
schemaName: string;
sourceHash: string;
} & ParsedActionSchemaGroupJSON;

function collectTypes(
definitions: Record<string, SchemaTypeDefinition>,
type: SchemaType,
Expand Down Expand Up @@ -60,15 +54,22 @@ function collectTypes(
}
}

function toJSONActionSchemaGroup(
/**
* Convert a ParsedActionSchema to a JSON-able object
* Data in the original ParsedActionSchema will not be modified.
*
* @param parsedActionSchema ParsedActionSchema to convert
* @returns
*/
export function toJSONParsedActionSchema(
parsedActionSchema: ParsedActionSchema,
): ParsedActionSchemaGroupJSON {
): ParsedActionSchemaJSON {
const definitions: Record<string, SchemaTypeDefinition> = {};
// clone it so we can modified it.
const entry = structuredClone(parsedActionSchema.entry);
definitions[entry.name] = entry;
collectTypes(definitions, entry.type);
const result: ParsedActionSchemaGroupJSON = {
const result: ParsedActionSchemaJSON = {
entry: entry.name,
types: definitions,
};
Expand All @@ -81,24 +82,6 @@ function toJSONActionSchemaGroup(
return result;
}

/**
* Convert a ActionSchemaFile to a JSON-able object
* Data in the original ActionSchemaFile will not be modified.
*
* @param actionSchemaFile ActionSchemaFile to convert
* @returns
*/
export function toJSONActionSchemaFile(
actionSchemaFile: ActionSchemaFile,
): ActionSchemaFileJSON {
const result: ActionSchemaFileJSON = {
schemaName: actionSchemaFile.schemaName,
sourceHash: actionSchemaFile.sourceHash,
...toJSONActionSchemaGroup(actionSchemaFile),
};
return result;
}

function resolveTypes(
definitions: Record<string, SchemaTypeDefinition>,
type: SchemaType,
Expand Down Expand Up @@ -133,16 +116,18 @@ function resolveTypes(
}

/**
* Convert a ActionSchemaFileJSON back to a ActionSchemaFile
* Convert a ParsedActionSchemaJSON back to a ParsedActionSchema
* Data in the JSON will be modified.
* Clone the data before passing into this function if you want to keep the original.
*
* @param json JSON data to convert
* @param schemaName Name of the schema (for error messages)
* @returns
*/
export function fromJSONActionSchemaFile(
json: ActionSchemaFileJSON,
): ActionSchemaFile {
export function fromJSONParsedActionSchema(
json: ParsedActionSchemaJSON,
schemaName: string,
): ParsedActionSchema {
for (const type of Object.values(json.types)) {
resolveTypes(json.types, type.type);
}
Expand All @@ -154,38 +139,11 @@ export function fromJSONActionSchemaFile(
actionNamespace: json.actionNamespace,
}
: undefined;
return createActionSchemaFile(
json.schemaName,
json.sourceHash,
return createParsedActionSchema(
schemaName,
entry,
order,
true,
schemaConfig,
);
}

export function loadParsedActionSchema(
schemaName: string,
schemaType: string,
sourceHash: string,
source: string,
): ActionSchemaFile {
const json = JSON.parse(source);
// TODO: validate the json
json.schemaName = schemaName;
json.sourceHash = sourceHash;
const actionSchemaFile = fromJSONActionSchemaFile(json);
if (actionSchemaFile.entry.name !== schemaType) {
throw new Error(
`Schema type mismatch: ${actionSchemaFile.entry.name} != ${schemaType}`,
);
}
return actionSchemaFile;
}

export function saveParsedActionSchema(
parsedActionSchema: ParsedActionSchema,
): string {
const json = toJSONActionSchemaGroup(parsedActionSchema);
return JSON.stringify(json);
}
8 changes: 0 additions & 8 deletions ts/packages/actionSchema/src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,3 @@ export type ParsedActionSchema = ActionSchemaGroup & {
// separate the cache by action name
actionNamespace?: boolean; // default to false
};

export type ActionSchemaFile = ParsedActionSchema & {
// Schema name
schemaName: string;

// original file source hash
sourceHash: string;
};
4 changes: 0 additions & 4 deletions ts/packages/actionSchema/test/parse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ describe("Action Schema Strict Checks", () => {
parseActionSchemaSource(
`type SomeAction = { actionName: "someAction" }`,
"test",
"",
"SomeAction",
"",
undefined,
Expand All @@ -24,7 +23,6 @@ describe("Action Schema Strict Checks", () => {
parseActionSchemaSource(
`// comments\nexport type AllActions = SomeAction;\ntype SomeAction = { actionName: "someAction" }`,
"test",
"",
"AllActions",
"",
undefined,
Expand All @@ -39,7 +37,6 @@ describe("Action Schema Strict Checks", () => {
parseActionSchemaSource(
`export type AllActions = SomeAction | SomeAction2;\ntype SomeAction = { actionName: "someAction" }\ntype SomeAction2 = { actionName: "someAction" }`,
"test",
"",
"AllActions",
"",
undefined,
Expand All @@ -54,7 +51,6 @@ describe("Action Schema Strict Checks", () => {
parseActionSchemaSource(
`export type AllActions = SomeAction | { actionName: "someAction2" };\ntype SomeAction = { actionName: "someAction" }`,
"test",
"",
"AllActions",
"",
undefined,
Expand Down
18 changes: 8 additions & 10 deletions ts/packages/actionSchema/test/regen.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import fs from "node:fs";
import path from "node:path";
import { parseActionSchemaSource } from "../src/parser.js";
import {
toJSONActionSchemaFile,
fromJSONActionSchemaFile,
toJSONParsedActionSchema,
fromJSONParsedActionSchema,
} from "../src/serialize.js";
import { fileURLToPath } from "node:url";
import { generateActionSchema } from "../src/generator.js";
Expand Down Expand Up @@ -119,7 +119,6 @@ describe("Action Schema Regeneration", () => {
const actionSchemaFile = parseActionSchemaSource(
source,
schemaName,
"testHash", // Don't care about source hash in tests
typeName,
fileName,
schemaConfig,
Expand All @@ -138,7 +137,6 @@ describe("Action Schema Regeneration", () => {
const actionSchemaFile = parseActionSchemaSource(
source,
schemaName,
"testHash", // Don't care about source hash in tests
typeName,
fileName,
);
Expand All @@ -147,7 +145,6 @@ describe("Action Schema Regeneration", () => {
const roundtrip = parseActionSchemaSource(
regenerated,
schemaName,
"testHash", // Don't care about source hash in tests
typeName,
);
const schema2 = await generateActionSchema(roundtrip);
Expand All @@ -163,22 +160,23 @@ describe("Action Schema Serialization", () => {
const actionSchemaFile = parseActionSchemaSource(
source,
schemaName,
"testHash", // Don't care about source hash in tests
typeName,
fileName,
);
const serialized = toJSONActionSchemaFile(actionSchemaFile);
const deserialized = fromJSONActionSchemaFile(
const serialized = toJSONParsedActionSchema(actionSchemaFile);
const deserialized = fromJSONParsedActionSchema(
structuredClone(serialized),
schemaName,
);

expect(deserialized).toEqual(actionSchemaFile);

const serialized2 = toJSONActionSchemaFile(actionSchemaFile);
const serialized2 = toJSONParsedActionSchema(actionSchemaFile);
expect(serialized2).toEqual(serialized);

const deserialized2 = fromJSONActionSchemaFile(
const deserialized2 = fromJSONParsedActionSchema(
structuredClone(serialized),
schemaName,
);
expect(deserialized2).toEqual(deserialized);
},
Expand Down
5 changes: 2 additions & 3 deletions ts/packages/actionSchemaCompiler/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Command, Flags } from "@oclif/core";
import {
parseActionSchemaSource,
SchemaConfig,
saveParsedActionSchema,
toJSONParsedActionSchema,
} from "action-schema";
import path from "node:path";
import fs from "node:fs";
Expand Down Expand Up @@ -51,7 +51,6 @@ export default class Compile extends Command {
const actionSchemaFile = parseActionSchemaSource(
fs.readFileSync(flags.input, "utf-8"),
name,
"",
flags.schemaType,
flags.input,
getSchemaConfig(flags.input),
Expand All @@ -67,7 +66,7 @@ export default class Compile extends Command {
}
fs.writeFileSync(
flags.output,
saveParsedActionSchema(actionSchemaFile),
JSON.stringify(toJSONParsedActionSchema(actionSchemaFile)),
);
console.log(`Parsed action schema written: ${flags.output}`);
}
Expand Down
Loading
Loading