@omnixdp/typegen 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # @omnixdp/typegen
2
+
3
+ Generate TypeScript types for Omni xDP SDK responses from live CMS, Business Objects, and Ratings and Reviews schemas.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -D @omnixdp/typegen
9
+ ```
10
+
11
+ ## Generate
12
+
13
+ ```bash
14
+ npx omnixdp-typegen \
15
+ --api-url https://api.example.com \
16
+ --management-token "$OMNIXDP_MANAGEMENT_TOKEN" \
17
+ --business-objects-token "$OMNIXDP_BO_TOKEN" \
18
+ --ratings-and-reviews-token "$OMNIXDP_RR_TOKEN" \
19
+ --cms-space marketing \
20
+ --bo-space products \
21
+ --rr-space storefront-reviews \
22
+ --out src/omnixdp.generated.ts
23
+ ```
24
+
25
+ Space arguments can be a space id, slug, or exact name. Regenerate the output after changing content types, taxonomy types, data types, or global fields in the admin.
26
+
27
+ ## Check in CI
28
+
29
+ ```bash
30
+ npx omnixdp-typegen --config omnixdp.typegen.config.js --check
31
+ ```
32
+
33
+ `--check` fails when the generated output is stale.
34
+
35
+ ## Config File
36
+
37
+ ```js
38
+ module.exports = {
39
+ apiUrl: process.env.OMNIXDP_API_URL,
40
+ managementToken: process.env.OMNIXDP_MANAGEMENT_TOKEN,
41
+ businessObjectsToken: process.env.OMNIXDP_BO_TOKEN,
42
+ ratingsAndReviewsToken: process.env.OMNIXDP_RR_TOKEN,
43
+ cmsSpace: "marketing",
44
+ boSpace: "products",
45
+ rrSpace: "storefront-reviews",
46
+ out: "src/omnixdp.generated.ts"
47
+ };
48
+ ```
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || function (mod) {
20
+ if (mod && mod.__esModule) return mod;
21
+ var result = {};
22
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
23
+ __setModuleDefault(result, mod);
24
+ return result;
25
+ };
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ const promises_1 = require("node:fs/promises");
28
+ const node_path_1 = require("node:path");
29
+ const node_url_1 = require("node:url");
30
+ const fetch_schema_1 = require("./fetch-schema");
31
+ const generate_1 = require("./generate");
32
+ async function main() {
33
+ const args = parseArgs(process.argv.slice(2));
34
+ if (args.help) {
35
+ printHelp();
36
+ return;
37
+ }
38
+ const fileConfig = args.config ? await loadConfig(args.config) : await loadDefaultConfig();
39
+ const config = compactConfig({ ...fileConfig, ...args });
40
+ const out = config.out ?? "src/omnixdp.generated.ts";
41
+ if (!config.apiUrl) {
42
+ throw new Error("--api-url or apiUrl in config is required");
43
+ }
44
+ const schema = await (0, fetch_schema_1.fetchTypegenSchema)(config);
45
+ const output = (0, generate_1.generateTypes)({ schema });
46
+ const outPath = (0, node_path_1.resolve)(process.cwd(), out);
47
+ if (args.check) {
48
+ const current = await (0, promises_1.readFile)(outPath, "utf8").catch(() => "");
49
+ if (current !== output) {
50
+ throw new Error(`${out} is out of date. Run omnixdp-typegen to regenerate it.`);
51
+ }
52
+ console.log(`${out} is up to date.`);
53
+ return;
54
+ }
55
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(outPath), { recursive: true });
56
+ await (0, promises_1.writeFile)(outPath, output, "utf8");
57
+ console.log(`Generated ${out}`);
58
+ }
59
+ function parseArgs(argv) {
60
+ const out = {};
61
+ for (let i = 0; i < argv.length; i += 1) {
62
+ const arg = argv[i];
63
+ if (arg === "--help" || arg === "-h") {
64
+ out.help = true;
65
+ continue;
66
+ }
67
+ if (arg === "--check") {
68
+ out.check = true;
69
+ continue;
70
+ }
71
+ if (!arg.startsWith("--")) {
72
+ throw new Error(`Unexpected argument: ${arg}`);
73
+ }
74
+ const key = arg.slice(2);
75
+ const value = argv[i + 1];
76
+ if (!value || value.startsWith("--")) {
77
+ throw new Error(`Missing value for ${arg}`);
78
+ }
79
+ i += 1;
80
+ assignArg(out, key, value);
81
+ }
82
+ return out;
83
+ }
84
+ function assignArg(out, key, value) {
85
+ switch (key) {
86
+ case "api-url":
87
+ out.apiUrl = value;
88
+ break;
89
+ case "management-token":
90
+ out.managementToken = value;
91
+ break;
92
+ case "business-objects-token":
93
+ out.businessObjectsToken = value;
94
+ break;
95
+ case "ratings-and-reviews-token":
96
+ out.ratingsAndReviewsToken = value;
97
+ break;
98
+ case "cms-space":
99
+ out.cmsSpace = value;
100
+ break;
101
+ case "bo-space":
102
+ out.boSpace = value;
103
+ break;
104
+ case "rr-space":
105
+ out.rrSpace = value;
106
+ break;
107
+ case "out":
108
+ out.out = value;
109
+ break;
110
+ case "config":
111
+ out.config = value;
112
+ break;
113
+ default:
114
+ throw new Error(`Unknown option: --${key}`);
115
+ }
116
+ }
117
+ async function loadDefaultConfig() {
118
+ for (const file of ["omnixdp.typegen.config.ts", "omnixdp.typegen.config.js", "omnixdp.typegen.config.cjs", "omnixdp.typegen.config.json"]) {
119
+ const config = await loadConfig(file).catch((error) => {
120
+ if (error.code === "ENOENT")
121
+ return null;
122
+ throw error;
123
+ });
124
+ if (config)
125
+ return config;
126
+ }
127
+ return {};
128
+ }
129
+ async function loadConfig(file) {
130
+ const path = (0, node_path_1.resolve)(process.cwd(), file);
131
+ if (path.endsWith(".json")) {
132
+ return JSON.parse(await (0, promises_1.readFile)(path, "utf8"));
133
+ }
134
+ const imported = await Promise.resolve(`${(0, node_url_1.pathToFileURL)(path).href}`).then(s => __importStar(require(s)));
135
+ return (imported.default ?? imported.config ?? imported);
136
+ }
137
+ function compactConfig(config) {
138
+ return Object.fromEntries(Object.entries(config).filter(([, value]) => value !== undefined && value !== ""));
139
+ }
140
+ function printHelp() {
141
+ console.log(`Usage: omnixdp-typegen --api-url <url> [options]
142
+
143
+ Options:
144
+ --config <path> Load omnixdp typegen config
145
+ --management-token <token> CMS Management API bearer token
146
+ --business-objects-token <token> Business Objects API bearer token
147
+ --ratings-and-reviews-token <token> Ratings and Reviews API bearer token
148
+ --cms-space <id|slug|name> CMS space to generate
149
+ --bo-space <id|slug|name> Business Objects space to generate
150
+ --rr-space <id|slug|name> Ratings and Reviews space to generate
151
+ --out <path> Output file, default src/omnixdp.generated.ts
152
+ --check Fail if the output file is stale
153
+ `);
154
+ }
155
+ main().catch((error) => {
156
+ console.error(error instanceof Error ? error.message : String(error));
157
+ process.exitCode = 1;
158
+ });
@@ -0,0 +1,3 @@
1
+ import type { TypegenConfig, TypegenSchema } from "./schema";
2
+ export declare function normalizeApiUrl(apiUrl: string): string;
3
+ export declare function fetchTypegenSchema(config: TypegenConfig): Promise<TypegenSchema>;
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeApiUrl = normalizeApiUrl;
4
+ exports.fetchTypegenSchema = fetchTypegenSchema;
5
+ function normalizeApiUrl(apiUrl) {
6
+ const trimmed = apiUrl.trim().replace(/\/+$/, "");
7
+ if (!trimmed)
8
+ throw new Error("apiUrl is required");
9
+ return trimmed.endsWith("/v1") ? trimmed : `${trimmed}/v1`;
10
+ }
11
+ async function fetchTypegenSchema(config) {
12
+ const apiBase = normalizeApiUrl(config.apiUrl);
13
+ const [cms, businessObjects, ratingsAndReviews] = await Promise.all([
14
+ config.managementToken && config.cmsSpace
15
+ ? fetchCmsSchema(apiBase, config.managementToken, config.cmsSpace)
16
+ : Promise.resolve({ contentTypes: [], taxonomyTypes: [] }),
17
+ config.businessObjectsToken && config.boSpace
18
+ ? fetchBoSchema(apiBase, "business-objects", config.businessObjectsToken, config.boSpace)
19
+ : Promise.resolve({ dataTypes: [] }),
20
+ config.ratingsAndReviewsToken && config.rrSpace
21
+ ? fetchBoSchema(apiBase, "ratings-and-reviews", config.ratingsAndReviewsToken, config.rrSpace)
22
+ : Promise.resolve({ dataTypes: [] })
23
+ ]);
24
+ const canonical = JSON.stringify({ cms, businessObjects, ratingsAndReviews });
25
+ return {
26
+ generatedAt: new Date().toISOString(),
27
+ sourceHash: await hashText(canonical),
28
+ cms,
29
+ businessObjects,
30
+ ratingsAndReviews
31
+ };
32
+ }
33
+ async function fetchCmsSchema(apiBase, token, spaceRef) {
34
+ const space = await resolveSpace(apiBase, "management", token, spaceRef);
35
+ const [contentTypes, taxonomyTypes] = await Promise.all([
36
+ fetchModels(apiBase, "management", token, "content-types", space.id),
37
+ fetchModels(apiBase, "management", token, "taxonomy-types", space.id)
38
+ ]);
39
+ return { space, contentTypes, taxonomyTypes };
40
+ }
41
+ async function fetchBoSchema(apiBase, app, token, spaceRef) {
42
+ const space = await resolveSpace(apiBase, app, token, spaceRef);
43
+ const dataTypes = await fetchModels(apiBase, app, token, "data-types", space.id);
44
+ return { space, dataTypes };
45
+ }
46
+ async function resolveSpace(apiBase, app, token, spaceRef) {
47
+ const spaces = await apiGet(apiBase, app, token, "spaces", { limit: "500" });
48
+ const wanted = spaceRef.trim();
49
+ const match = spaces.find((space) => space.id === wanted || space.slug === wanted || space.name === wanted);
50
+ if (!match?.id || !match.name || !match.slug) {
51
+ throw new Error(`Space "${spaceRef}" was not found in ${app}`);
52
+ }
53
+ return { id: match.id, name: match.name, slug: match.slug };
54
+ }
55
+ async function fetchModels(apiBase, app, token, resource, spaceId) {
56
+ const rows = await apiGet(apiBase, app, token, resource, { spaceId, limit: "500", includeArchived: "1" });
57
+ return rows
58
+ .filter((row) => Boolean(row.id && row.name && row.apiIdentifier))
59
+ .map((row) => ({
60
+ id: row.id,
61
+ name: row.name,
62
+ apiIdentifier: row.apiIdentifier,
63
+ fields: normalizeFields(row.fields ?? [])
64
+ }));
65
+ }
66
+ async function apiGet(apiBase, app, token, resource, query) {
67
+ const url = new URL(`${apiBase}/${app}/${resource}`);
68
+ for (const [key, value] of Object.entries(query)) {
69
+ url.searchParams.set(key, value);
70
+ }
71
+ const response = await fetch(url, {
72
+ headers: {
73
+ accept: "application/json",
74
+ authorization: `Bearer ${token}`
75
+ }
76
+ });
77
+ const body = (await response.json().catch(() => null));
78
+ if (!response.ok || !body || body.success !== true) {
79
+ const message = body && "error" in body ? body.error?.message : undefined;
80
+ throw new Error(`Failed to fetch ${app}/${resource}: ${message ?? response.statusText}`);
81
+ }
82
+ return body.data;
83
+ }
84
+ function normalizeFields(fields) {
85
+ return fields
86
+ .filter((field) => field.name && field.apiIdentifier && field.type)
87
+ .map((field) => {
88
+ const validations = isRecord(field.validations) ? field.validations : null;
89
+ return {
90
+ id: field.id,
91
+ name: field.name,
92
+ apiIdentifier: field.apiIdentifier,
93
+ type: field.type,
94
+ required: field.required === true,
95
+ multiple: field.multiple === true || validations?.__multiple === true,
96
+ localizable: field.localizable === true || validations?.__localizable === true,
97
+ deliveryApi: field.deliveryApi === true || validations?.__deliveryApi === true,
98
+ validations,
99
+ fields: nestedFields(field, validations),
100
+ globalFieldApiIdentifier: field.globalField?.apiIdentifier,
101
+ referenceTarget: readReferenceTarget(validations)
102
+ };
103
+ });
104
+ }
105
+ function nestedFields(field, validations) {
106
+ if (Array.isArray(field.globalField?.fields))
107
+ return normalizeFields(field.globalField.fields);
108
+ const groupSchema = isRecord(validations?.groupSchema) ? validations.groupSchema : undefined;
109
+ const blockSchema = isRecord(validations?.blockSchema) ? validations.blockSchema : undefined;
110
+ const schema = groupSchema ?? blockSchema;
111
+ return Array.isArray(schema?.fields) ? normalizeFields(schema.fields) : undefined;
112
+ }
113
+ function readReferenceTarget(validations) {
114
+ const raw = validations?.referenceTarget;
115
+ if (!isRecord(raw))
116
+ return undefined;
117
+ return {
118
+ spaceId: typeof raw.spaceId === "string" ? raw.spaceId : undefined,
119
+ contentTypeId: typeof raw.contentTypeId === "string" ? raw.contentTypeId : undefined,
120
+ dataTypeId: typeof raw.dataTypeId === "string" ? raw.dataTypeId : undefined
121
+ };
122
+ }
123
+ function isRecord(value) {
124
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
125
+ }
126
+ async function hashText(text) {
127
+ const bytes = new TextEncoder().encode(text);
128
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
129
+ return Array.from(new Uint8Array(digest))
130
+ .map((byte) => byte.toString(16).padStart(2, "0"))
131
+ .join("");
132
+ }
@@ -0,0 +1,5 @@
1
+ import type { TypegenSchema } from "./schema";
2
+ export type GenerateTypesOptions = {
3
+ schema: TypegenSchema;
4
+ };
5
+ export declare function generateTypes({ schema }: GenerateTypesOptions): string;
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateTypes = generateTypes;
4
+ function generateTypes({ schema }) {
5
+ const sections = [
6
+ renderHeader(schema),
7
+ renderSharedTypes(),
8
+ renderModelTypes("Cms", "ContentTypes", schema.cms.contentTypes),
9
+ renderModelTypes("Cms", "TaxonomyTypes", schema.cms.taxonomyTypes),
10
+ renderModelTypes("BusinessObject", "DataTypes", schema.businessObjects.dataTypes),
11
+ renderModelTypes("RatingsReview", "DataTypes", schema.ratingsAndReviews.dataTypes),
12
+ renderConvenienceAliases(schema)
13
+ ].filter(Boolean);
14
+ return `${sections.join("\n\n")}\n`;
15
+ }
16
+ function renderHeader(schema) {
17
+ const lines = [
18
+ "/* eslint-disable */",
19
+ "// This file is generated by @omnixdp/typegen. Do not edit it manually.",
20
+ `// Generated at: ${schema.generatedAt}`,
21
+ `// Schema hash: ${schema.sourceHash}`
22
+ ];
23
+ if (usesDeliveryTypes(schema)) {
24
+ lines.push("", "import type { DeliveryAsset, DeliveryEntry, DeliveryTaxonomyResponse } from \"@omnixdp/delivery\";");
25
+ }
26
+ return lines.join("\n");
27
+ }
28
+ function renderSharedTypes() {
29
+ return [
30
+ "export type OmnixdpJsonPrimitive = string | number | boolean | null;",
31
+ "export type OmnixdpJsonValue = OmnixdpJsonPrimitive | OmnixdpJsonValue[] | { [key: string]: OmnixdpJsonValue };",
32
+ "export type OmnixdpUploadValue = {",
33
+ " id?: string;",
34
+ " fileName?: string;",
35
+ " url?: string;",
36
+ " mimeType?: string | null;",
37
+ " sizeBytes?: number | null;",
38
+ " [key: string]: OmnixdpJsonValue | undefined;",
39
+ "};",
40
+ "export type OmnixdpRichTextValue = string | {",
41
+ " type: \"doc\";",
42
+ " content?: OmnixdpJsonValue[];",
43
+ " [key: string]: OmnixdpJsonValue | undefined;",
44
+ "};",
45
+ "export type OmnixdpDataTreeNode<TData> = {",
46
+ " id?: string;",
47
+ " data: TData;",
48
+ " children?: OmnixdpDataTreeNode<TData>[];",
49
+ " __fieldMeta?: Record<string, unknown>;",
50
+ "};",
51
+ "export type OmnixdpDataTreeValue<TData, TAggregation = Record<string, number>> = {",
52
+ " nodes: OmnixdpDataTreeNode<TData>[];",
53
+ " aggregation?: TAggregation & { children?: number };",
54
+ "};"
55
+ ].join("\n");
56
+ }
57
+ function renderModelTypes(prefix, suffix, models) {
58
+ const typeBlocks = models.flatMap((model) => {
59
+ const typeName = modelTypeName(prefix, model);
60
+ return [
61
+ `export type ${typeName} = ${renderObjectType(model.fields, 0)};`
62
+ ];
63
+ });
64
+ const mapName = `${prefix}${suffix}`;
65
+ const mapLines = [
66
+ `export type ${mapName} = {`,
67
+ ...models.map((model) => ` ${quoteKey(model.apiIdentifier)}: ${modelTypeName(prefix, model)};`),
68
+ "};"
69
+ ];
70
+ return [...typeBlocks, mapLines.join("\n")].join("\n\n");
71
+ }
72
+ function renderConvenienceAliases(schema) {
73
+ const lines = [];
74
+ if (schema.cms.contentTypes.length > 0) {
75
+ lines.push("export type CmsDeliveryEntry<TApiIdentifier extends keyof CmsContentTypes> = DeliveryEntry<CmsContentTypes[TApiIdentifier]>;");
76
+ }
77
+ if (schema.cms.taxonomyTypes.length > 0) {
78
+ lines.push("export type CmsTaxonomyResponse<TApiIdentifier extends keyof CmsTaxonomyTypes> = DeliveryTaxonomyResponse & {", " tree: Array<DeliveryTaxonomyResponse[\"tree\"][number] & { data?: CmsTaxonomyTypes[TApiIdentifier] | null }>;", "};");
79
+ }
80
+ if (schema.businessObjects.dataTypes.length > 0) {
81
+ lines.push("export type BusinessObjectDataObject<TApiIdentifier extends keyof BusinessObjectDataTypes> = {", " id: string;", " data: BusinessObjectDataTypes[TApiIdentifier];", " [key: string]: unknown;", "};");
82
+ }
83
+ if (schema.ratingsAndReviews.dataTypes.length > 0) {
84
+ lines.push("export type RatingsReviewDataObject<TApiIdentifier extends keyof RatingsReviewDataTypes> = {", " id: string;", " data: RatingsReviewDataTypes[TApiIdentifier];", " [key: string]: unknown;", "};");
85
+ }
86
+ if (schema.cms.contentTypes.length === 0 && schema.businessObjects.dataTypes.length === 0 && schema.ratingsAndReviews.dataTypes.length === 0) {
87
+ lines.push("// No models were fetched. Check the selected spaces and API tokens.");
88
+ }
89
+ return lines.join("\n");
90
+ }
91
+ function renderObjectType(fields, level) {
92
+ if (fields.length === 0)
93
+ return "Record<string, never>";
94
+ const indent = " ".repeat(level);
95
+ const nextIndent = " ".repeat(level + 1);
96
+ return [
97
+ "{",
98
+ ...fields.map((field) => `${nextIndent}${quoteKey(field.apiIdentifier)}${field.required ? "" : "?"}: ${renderFieldType(field, level + 1)};`),
99
+ `${indent}}`
100
+ ].join("\n");
101
+ }
102
+ function renderFieldType(field, level) {
103
+ const base = renderSingleFieldType(field, level);
104
+ return field.multiple && field.type !== "DATA_TREE" ? `Array<${base}>` : base;
105
+ }
106
+ function renderSingleFieldType(field, level) {
107
+ switch (field.type) {
108
+ case "TEXT":
109
+ case "MULTI_LINE_TEXT":
110
+ case "TITLE":
111
+ case "SLUG":
112
+ case "LINK":
113
+ case "URL":
114
+ case "MARKDOWN":
115
+ case "HTML":
116
+ case "CSS":
117
+ case "JAVASCRIPT":
118
+ case "XML":
119
+ case "REFERENCE":
120
+ case "CMS_ENTRY":
121
+ case "CMS_TAXONOMY":
122
+ case "RR_REVIEW":
123
+ case "RR_CATALOG":
124
+ case "BO_DATA_OBJECT":
125
+ case "BO_CATALOG":
126
+ case "COLOR":
127
+ return "string";
128
+ case "SELECT":
129
+ return optionUnion(field) ?? "string";
130
+ case "RADIO":
131
+ return `Array<${optionUnion(field) ?? "string"}>`;
132
+ case "NUMBER":
133
+ case "MONEY":
134
+ case "AGGREGATION":
135
+ return "number";
136
+ case "BOOLEAN":
137
+ return "boolean";
138
+ case "DATE":
139
+ return "string";
140
+ case "RICHTEXT":
141
+ return "OmnixdpRichTextValue";
142
+ case "JSON":
143
+ return "OmnixdpJsonValue";
144
+ case "MEDIA":
145
+ return "string | DeliveryAsset | null";
146
+ case "UPLOAD":
147
+ return "string | OmnixdpUploadValue | null";
148
+ case "GLOBALFIELD":
149
+ case "GROUP":
150
+ case "BLOCK":
151
+ case "MODULAR_BLOCK":
152
+ return field.fields?.length ? renderObjectType(field.fields, level) : "Record<string, unknown>";
153
+ case "DATA_TREE": {
154
+ const nested = field.fields ?? [];
155
+ const dataFields = nested.filter((child) => child.type !== "AGGREGATION");
156
+ const aggregationFields = nested.filter((child) => child.type === "AGGREGATION");
157
+ const dataType = dataFields.length ? renderObjectType(dataFields, level) : "Record<string, unknown>";
158
+ const aggregationType = aggregationFields.length ? renderObjectType(aggregationFields, level) : "Record<string, number>";
159
+ return `OmnixdpDataTreeValue<${dataType}, ${aggregationType}>`;
160
+ }
161
+ default:
162
+ return "unknown";
163
+ }
164
+ }
165
+ function optionUnion(field) {
166
+ const values = optionValues(field.validations);
167
+ if (values.length === 0)
168
+ return null;
169
+ return values.map((value) => JSON.stringify(value)).join(" | ");
170
+ }
171
+ function optionValues(validations) {
172
+ if (!validations)
173
+ return [];
174
+ const fromOptions = Array.isArray(validations.options)
175
+ ? validations.options
176
+ .map((item) => (isRecord(item) && typeof item.value === "string" ? item.value.trim() : ""))
177
+ .filter(Boolean)
178
+ : [];
179
+ if (fromOptions.length > 0)
180
+ return unique(fromOptions);
181
+ const fromRadioValues = Array.isArray(validations.radioValues)
182
+ ? validations.radioValues.map((item) => (typeof item === "string" ? item.trim() : "")).filter(Boolean)
183
+ : [];
184
+ if (fromRadioValues.length > 0)
185
+ return unique(fromRadioValues);
186
+ const labels = isRecord(validations.selectItemsLocales)
187
+ ? Object.values(validations.selectItemsLocales)
188
+ .flatMap((items) => (Array.isArray(items) ? items : []))
189
+ .map((item) => (typeof item === "string" ? item.trim() : ""))
190
+ .filter(Boolean)
191
+ : [];
192
+ return unique(labels);
193
+ }
194
+ function modelTypeName(prefix, model) {
195
+ return `${prefix}${pascalCase(model.apiIdentifier)}`;
196
+ }
197
+ function pascalCase(value) {
198
+ const words = value.split(/[^a-zA-Z0-9]+/).filter(Boolean);
199
+ const name = words.map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`).join("");
200
+ return name || "Model";
201
+ }
202
+ function quoteKey(value) {
203
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value) ? value : JSON.stringify(value);
204
+ }
205
+ function unique(values) {
206
+ return [...new Set(values)];
207
+ }
208
+ function isRecord(value) {
209
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
210
+ }
211
+ function usesDeliveryTypes(schema) {
212
+ return (schema.cms.contentTypes.length > 0 ||
213
+ schema.cms.taxonomyTypes.length > 0 ||
214
+ schema.businessObjects.dataTypes.some((model) => model.fields.some(fieldUsesDeliveryAsset)) ||
215
+ schema.ratingsAndReviews.dataTypes.some((model) => model.fields.some(fieldUsesDeliveryAsset)));
216
+ }
217
+ function fieldUsesDeliveryAsset(field) {
218
+ return field.type === "MEDIA" || Boolean(field.fields?.some(fieldUsesDeliveryAsset));
219
+ }
@@ -0,0 +1,3 @@
1
+ export { fetchTypegenSchema, normalizeApiUrl } from "./fetch-schema";
2
+ export { generateTypes } from "./generate";
3
+ export type { TypegenApplication, TypegenConfig, TypegenField, TypegenFieldKind, TypegenModel, TypegenSchema, TypegenSpace } from "./schema";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateTypes = exports.normalizeApiUrl = exports.fetchTypegenSchema = void 0;
4
+ var fetch_schema_1 = require("./fetch-schema");
5
+ Object.defineProperty(exports, "fetchTypegenSchema", { enumerable: true, get: function () { return fetch_schema_1.fetchTypegenSchema; } });
6
+ Object.defineProperty(exports, "normalizeApiUrl", { enumerable: true, get: function () { return fetch_schema_1.normalizeApiUrl; } });
7
+ var generate_1 = require("./generate");
8
+ Object.defineProperty(exports, "generateTypes", { enumerable: true, get: function () { return generate_1.generateTypes; } });
@@ -0,0 +1,58 @@
1
+ export type TypegenApplication = "cms" | "business-objects" | "ratings-and-reviews";
2
+ export type TypegenFieldKind = "TEXT" | "MULTI_LINE_TEXT" | "TITLE" | "SLUG" | "LINK" | "URL" | "NUMBER" | "MONEY" | "BOOLEAN" | "DATE" | "RICHTEXT" | "MARKDOWN" | "JSON" | "HTML" | "CSS" | "JAVASCRIPT" | "XML" | "REFERENCE" | "MEDIA" | "GLOBALFIELD" | "SELECT" | "RADIO" | "MODULAR_BLOCK" | "BLOCK" | "COLOR" | "GROUP" | "UPLOAD" | "RR_REVIEW" | "RR_CATALOG" | "BO_DATA_OBJECT" | "BO_CATALOG" | "CMS_ENTRY" | "CMS_TAXONOMY" | "DATA_TREE" | "AGGREGATION";
3
+ export type TypegenField = {
4
+ id?: string;
5
+ name: string;
6
+ apiIdentifier: string;
7
+ type: TypegenFieldKind | string;
8
+ required?: boolean;
9
+ multiple?: boolean;
10
+ localizable?: boolean;
11
+ deliveryApi?: boolean;
12
+ validations?: Record<string, unknown> | null;
13
+ fields?: TypegenField[];
14
+ globalFieldApiIdentifier?: string;
15
+ referenceTarget?: {
16
+ spaceId?: string;
17
+ contentTypeId?: string;
18
+ dataTypeId?: string;
19
+ };
20
+ };
21
+ export type TypegenModel = {
22
+ id: string;
23
+ name: string;
24
+ apiIdentifier: string;
25
+ fields: TypegenField[];
26
+ };
27
+ export type TypegenSpace = {
28
+ id: string;
29
+ name: string;
30
+ slug: string;
31
+ };
32
+ export type TypegenSchema = {
33
+ generatedAt: string;
34
+ sourceHash: string;
35
+ cms: {
36
+ space?: TypegenSpace;
37
+ contentTypes: TypegenModel[];
38
+ taxonomyTypes: TypegenModel[];
39
+ };
40
+ businessObjects: {
41
+ space?: TypegenSpace;
42
+ dataTypes: TypegenModel[];
43
+ };
44
+ ratingsAndReviews: {
45
+ space?: TypegenSpace;
46
+ dataTypes: TypegenModel[];
47
+ };
48
+ };
49
+ export type TypegenConfig = {
50
+ apiUrl: string;
51
+ managementToken?: string;
52
+ businessObjectsToken?: string;
53
+ ratingsAndReviewsToken?: string;
54
+ cmsSpace?: string;
55
+ boSpace?: string;
56
+ rrSpace?: string;
57
+ out?: string;
58
+ };
package/dist/schema.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@omnixdp/typegen",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript type generator for Omni xDP SDK response models.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/omnixdp/platform.git",
9
+ "directory": "packages/typegen"
10
+ },
11
+ "homepage": "https://github.com/omnixdp/platform/tree/main/packages/typegen#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/omnixdp/platform/issues"
14
+ },
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "bin": {
18
+ "omnixdp-typegen": "./dist/cli.js"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "require": "./dist/index.js",
24
+ "default": "./dist/index.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "README.md"
30
+ ],
31
+ "sideEffects": false,
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "scripts": {
36
+ "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
37
+ "build": "pnpm run clean && tsc -p tsconfig.build.json",
38
+ "typecheck": "tsc -p tsconfig.json --noEmit",
39
+ "test": "vitest run",
40
+ "prepublishOnly": "pnpm run build"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^22.7.4",
44
+ "typescript": "^5.6.3",
45
+ "vitest": "^4.1.5"
46
+ },
47
+ "engines": {
48
+ "node": ">=22.16.0"
49
+ }
50
+ }