diff --git a/ts/package.json b/ts/package.json index 59479e1..70dd763 100644 --- a/ts/package.json +++ b/ts/package.json @@ -28,7 +28,7 @@ "clean": "rm -rf dist", "typecheck": "tsc --noEmit", "test": "vitest run", - "build:definitions": "npx tsx scripts/build-definitions.ts -- --version def-0.0.30" + "build:definitions": "npx tsx scripts/build-definitions.ts -- --version def-0.0.34" }, "keywords": [], "author": "", diff --git a/ts/scripts/build-definitions.ts b/ts/scripts/build-definitions.ts index 63dc429..b3cd2f7 100644 --- a/ts/scripts/build-definitions.ts +++ b/ts/scripts/build-definitions.ts @@ -27,7 +27,7 @@ function toPascalCase(str: string): string { } function fileNameToClassName(file: string): string { - return toPascalCase(path.basename(file, ".proto.json")); + return toPascalCase(path.basename(file, ".json")); } function toSchemaName(identifier: string): string { @@ -74,7 +74,10 @@ function buildCombinedSource(defs: DataTypeDef[]): string { resolved = "Record"; } else { for (const key of genericKeys) { - resolved = resolved.replace(new RegExp(`\\b${key}\\b`, "g"), "unknown"); + // genericKeys entries may carry a constraint, e.g. "M extends TEXT" — + // only the bare parameter name should be substituted in the type body. + const paramName = key.split(/\s+extends\s+/)[0].trim(); + resolved = resolved.replace(new RegExp(`\\b${paramName}\\b`, "g"), "unknown"); } } return `export type ${identifier} = ${resolved};`; @@ -242,11 +245,13 @@ function walkDefs(dir: string, relModule: string, handler: DefHandler): void { walkDefs(fullPath, path.join(relModule, entry.name), handler); continue; } - if (!entry.name.endsWith(".proto.json")) continue; + if (!entry.name.endsWith(".json") || entry.name === "module.json") continue; const json = JSON.parse(fs.readFileSync(fullPath, "utf-8")) as Record; const typeFolder = relModule.split(path.sep).at(-1) ?? ""; const className = fileNameToClassName(entry.name); - const fileName = path.basename(entry.name, ".proto.json"); + // Upstream data type/flow filenames are UPPER_SNAKE (e.g. HTTP_METHOD.json); + // keep our generated tree lowercase to match the rest of the file naming convention. + const fileName = path.basename(entry.name, ".json").toLowerCase(); handler(json, className, fileName, relModule, typeFolder); } } @@ -327,8 +332,12 @@ async function main() { .filter(n => n !== schemaName && allSchemaNames.has(n)) ), ]; + // Constraints (e.g. "M extends TEXT") aren't emitted here: the schema + // body already has the constrained param resolved to `unknown`, so the + // generic is cosmetic only — keeping "extends X" would require importing + // X's type just to satisfy a bound nothing actually checks against. const typeParams = def.genericKeys.length > 0 - ? `<${def.genericKeys.map(k => `${k} = unknown`).join(", ")}>` + ? `<${def.genericKeys.map(k => `${k.split(/\s+extends\s+/)[0].trim()} = unknown`).join(", ")}>` : ""; const lines = [ diff --git a/ts/test/index.test.ts b/ts/test/index.test.ts index 1c0fa11..1a29578 100644 --- a/ts/test/index.test.ts +++ b/ts/test/index.test.ts @@ -5,7 +5,7 @@ import { EventSetting } from "../src/decorators/event.dec"; import { Identifier } from "../src/decorators/meta.dec"; import { eventMap } from "../src/map/event.map"; import { runtimeEventMap } from "../src/map/runtime_event.map"; -import { Rest } from "../src/definitions/draco_rest/runtime_flow_types/rest"; +import { Rest } from "../src/definitions/rest-action/runtime_flow_types/rest"; describe("Parameter decorator", () => { it("preserves source order across multiple decorators", () => { @@ -33,20 +33,20 @@ describe("Parameter decorator", () => { describe("eventMap", () => { const restSettingOrder = [ - "httpSchema", - "httpURL", - "httpMethod", - "httpAuth", - "httpAuthValue", + "http_schema", + "http_url", + "http_method", + "http_auth", + "http_auth_value", "input_schema", ]; it("keeps the runtime event's setting order regardless of override order", () => { @Identifier("ScrambledOverrides") @EventSetting({ identifier: "input_schema", hidden: true, defaultValue: {} }) - @EventSetting({ identifier: "httpAuthValue", hidden: true }) - @EventSetting({ identifier: "httpSchema", hidden: true, defaultValue: "application/json" }) - @EventSetting({ identifier: "httpMethod", hidden: true, defaultValue: "POST" }) + @EventSetting({ identifier: "http_auth_value", hidden: true }) + @EventSetting({ identifier: "http_schema", hidden: true, defaultValue: "application/json" }) + @EventSetting({ identifier: "http_method", hidden: true, defaultValue: "POST" }) class ScrambledOverrides extends Rest {} const def = eventMap(ScrambledOverrides); @@ -55,10 +55,10 @@ describe("eventMap", () => { it("merges override properties into the runtime event's setting", () => { @Identifier("MethodOverride") - @EventSetting({ identifier: "httpMethod", hidden: true, defaultValue: "POST" }) + @EventSetting({ identifier: "http_method", hidden: true, defaultValue: "POST" }) class MethodOverride extends Rest {} - const httpMethod = eventMap(MethodOverride).settings?.find(s => s.identifier === "httpMethod"); + const httpMethod = eventMap(MethodOverride).settings?.find(s => s.identifier === "http_method"); expect(httpMethod?.hidden).toBe(true); expect(httpMethod?.defaultValue).toBe("POST"); expect(httpMethod?.name).toEqual([{ code: "en-US", content: "Method" }]); @@ -66,14 +66,14 @@ describe("eventMap", () => { it("does not pollute the runtime event's settings via subclass overrides", () => { @Identifier("PollutionCheck") - @EventSetting({ identifier: "httpMethod", hidden: true, defaultValue: "POST" }) + @EventSetting({ identifier: "http_method", hidden: true, defaultValue: "POST" }) class PollutionCheck extends Rest {} eventMap(PollutionCheck); const restSettings = runtimeEventMap(Rest).settings ?? []; expect(restSettings.map(s => s.identifier)).toEqual(restSettingOrder); - expect(restSettings.find(s => s.identifier === "httpMethod")?.hidden).toBeUndefined(); + expect(restSettings.find(s => s.identifier === "http_method")?.hidden).toBeUndefined(); }); });