@cms0/cms0 0.2.7 → 0.2.8

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.
@@ -2,6 +2,7 @@
2
2
  import fs from "fs";
3
3
  import path from "path";
4
4
  import { Project, SyntaxKind, Node, } from "ts-morph";
5
+ import { computeUnionBranchKeys, } from "@cms0/shared";
5
6
  import { findTsConfig } from "./config-loader.js";
6
7
  import { customTypeNames, customInlineDescriptors, customInlineTypeNames, customModelDescriptors, customModelTypeNames, resolveCustomTypeDependencies, } from "../../custom-types/registry.js";
7
8
  function resolveCustomTypeName(type) {
@@ -121,6 +122,39 @@ function descriptorFromType(type, modelNames, warnings, ctx, opts) {
121
122
  const { base, optional, nullable } = unwrapOptional(type);
122
123
  const isOptional = !!opts?.optional || optional;
123
124
  const isNullable = !!opts?.nullable || nullable;
125
+ if (base.isUnion()) {
126
+ const unionDescriptor = descriptorFromUnionType(base, modelNames, warnings, ctx);
127
+ if (unionDescriptor) {
128
+ return applyOptionalityToDescriptor(unionDescriptor, isOptional, isNullable);
129
+ }
130
+ }
131
+ if (base.isStringLiteral()) {
132
+ return {
133
+ kind: "enum",
134
+ valueType: "string",
135
+ values: [String(base.getLiteralValue())],
136
+ optional: isOptional,
137
+ nullable: isNullable,
138
+ };
139
+ }
140
+ if (base.isNumberLiteral()) {
141
+ return {
142
+ kind: "enum",
143
+ valueType: "number",
144
+ values: [Number(base.getLiteralValue())],
145
+ optional: isOptional,
146
+ nullable: isNullable,
147
+ };
148
+ }
149
+ if (base.isBooleanLiteral()) {
150
+ return {
151
+ kind: "enum",
152
+ valueType: "boolean",
153
+ values: [base.getText() === "true"],
154
+ optional: isOptional,
155
+ nullable: isNullable,
156
+ };
157
+ }
124
158
  if (base.isString()) {
125
159
  return {
126
160
  kind: "primitive",
@@ -189,6 +223,203 @@ function descriptorFromType(type, modelNames, warnings, ctx, opts) {
189
223
  nullable: isNullable,
190
224
  };
191
225
  }
226
+ function descriptorFromUnionType(unionType, modelNames, warnings, ctx) {
227
+ const unionTypes = unionType.getUnionTypes();
228
+ if (!unionTypes.length)
229
+ return undefined;
230
+ const hasBroadString = unionTypes.some((entry) => entry.isString());
231
+ const hasBroadNumber = unionTypes.some((entry) => entry.isNumber());
232
+ const hasBroadBoolean = unionTypes.some((entry) => entry.isBoolean());
233
+ const filtered = unionTypes.filter((entry) => {
234
+ if (entry.isStringLiteral() && hasBroadString)
235
+ return false;
236
+ if (entry.isNumberLiteral() && hasBroadNumber)
237
+ return false;
238
+ if (entry.isBooleanLiteral() && hasBroadBoolean)
239
+ return false;
240
+ return true;
241
+ });
242
+ if (!filtered.length) {
243
+ if (hasBroadString) {
244
+ return { kind: "primitive", type: "string" };
245
+ }
246
+ if (hasBroadNumber) {
247
+ return { kind: "primitive", type: "number" };
248
+ }
249
+ if (hasBroadBoolean) {
250
+ return { kind: "primitive", type: "boolean" };
251
+ }
252
+ return undefined;
253
+ }
254
+ const literalEnum = resolveLiteralUnionAsEnum(filtered);
255
+ if (literalEnum) {
256
+ return literalEnum;
257
+ }
258
+ const branches = filtered
259
+ .map((entry, index) => descriptorFromType(entry, modelNames, warnings, `${ctx}|${index}`, {
260
+ optional: false,
261
+ nullable: false,
262
+ }))
263
+ .map((entry) => stripOptionality(entry));
264
+ const deduped = dedupeDescriptors(branches);
265
+ if (!deduped.length)
266
+ return undefined;
267
+ if (deduped.length === 1)
268
+ return deduped[0];
269
+ const discriminator = inferUnionDiscriminator(deduped);
270
+ const branchKeys = computeUnionBranchKeys(deduped);
271
+ return {
272
+ kind: "union",
273
+ anyOf: deduped,
274
+ branchKeys,
275
+ ...(discriminator ? { discriminator: { key: discriminator } } : {}),
276
+ };
277
+ }
278
+ function stripOptionality(descriptor) {
279
+ const clone = JSON.parse(JSON.stringify(descriptor));
280
+ delete clone.optional;
281
+ delete clone.nullable;
282
+ return clone;
283
+ }
284
+ function dedupeDescriptors(descriptors) {
285
+ const seen = new Set();
286
+ const deduped = [];
287
+ for (const descriptor of descriptors) {
288
+ const key = JSON.stringify(descriptor);
289
+ if (seen.has(key))
290
+ continue;
291
+ seen.add(key);
292
+ deduped.push(descriptor);
293
+ }
294
+ return deduped;
295
+ }
296
+ function resolveLiteralUnionAsEnum(types) {
297
+ if (!types.length)
298
+ return undefined;
299
+ const literals = [];
300
+ let kind;
301
+ for (const entry of types) {
302
+ if (entry.isStringLiteral()) {
303
+ if (kind && kind !== "string")
304
+ return undefined;
305
+ kind = "string";
306
+ literals.push(String(entry.getLiteralValue()));
307
+ continue;
308
+ }
309
+ if (entry.isNumberLiteral()) {
310
+ if (kind && kind !== "number")
311
+ return undefined;
312
+ kind = "number";
313
+ literals.push(Number(entry.getLiteralValue()));
314
+ continue;
315
+ }
316
+ if (entry.isBooleanLiteral()) {
317
+ if (kind && kind !== "boolean")
318
+ return undefined;
319
+ kind = "boolean";
320
+ literals.push(entry.getText() === "true");
321
+ continue;
322
+ }
323
+ return undefined;
324
+ }
325
+ if (!kind)
326
+ return undefined;
327
+ const unique = Array.from(new Set(literals));
328
+ return {
329
+ kind: "enum",
330
+ valueType: kind,
331
+ values: unique,
332
+ };
333
+ }
334
+ function inferUnionDiscriminator(descriptors) {
335
+ if (!descriptors.length ||
336
+ descriptors.some((entry) => entry?.type !== "object")) {
337
+ return undefined;
338
+ }
339
+ const objects = descriptors;
340
+ const sharedKeys = Object.keys(objects[0]?.properties ?? {}).filter((key) => objects.every((obj) => Object.prototype.hasOwnProperty.call(obj.properties ?? {}, key)));
341
+ for (const key of sharedKeys) {
342
+ const enumLikeValues = objects.map((obj) => {
343
+ const prop = (obj.properties ?? {})[key];
344
+ if (!prop)
345
+ return undefined;
346
+ if (prop.kind !== "enum" || !Array.isArray(prop.values) || prop.values.length !== 1) {
347
+ return undefined;
348
+ }
349
+ return prop.values[0];
350
+ });
351
+ if (enumLikeValues.some((value) => value === undefined)) {
352
+ continue;
353
+ }
354
+ const unique = new Set(enumLikeValues);
355
+ if (unique.size === enumLikeValues.length) {
356
+ return key;
357
+ }
358
+ }
359
+ return undefined;
360
+ }
361
+ function normalizePropertyOrder(keys, explicitOrder) {
362
+ const normalizedExplicit = Array.isArray(explicitOrder)
363
+ ? explicitOrder
364
+ .filter((value) => typeof value === "string")
365
+ .map((value) => value.trim())
366
+ .filter(Boolean)
367
+ : [];
368
+ const dedupedExplicit = Array.from(new Set(normalizedExplicit)).filter((key) => keys.includes(key));
369
+ const remaining = keys.filter((key) => !dedupedExplicit.includes(key));
370
+ return [...dedupedExplicit, ...remaining];
371
+ }
372
+ function applyFieldPropertyOrder(descriptor) {
373
+ if (!descriptor || typeof descriptor !== "object")
374
+ return descriptor;
375
+ if (descriptor.type === "object") {
376
+ const properties = (descriptor.properties ?? {});
377
+ const keys = Object.keys(properties);
378
+ for (const key of keys) {
379
+ properties[key] = applyFieldPropertyOrder(properties[key]);
380
+ }
381
+ descriptor.propertyOrder = normalizePropertyOrder(keys, descriptor.propertyOrder);
382
+ return descriptor;
383
+ }
384
+ if (descriptor.type === "array" && descriptor.items) {
385
+ descriptor.items = applyFieldPropertyOrder(descriptor.items);
386
+ return descriptor;
387
+ }
388
+ if (descriptor.kind === "union" && Array.isArray(descriptor.anyOf)) {
389
+ descriptor.anyOf = descriptor.anyOf.map((branch) => applyFieldPropertyOrder(branch));
390
+ return descriptor;
391
+ }
392
+ return descriptor;
393
+ }
394
+ function applyDescriptorOrderingMetadata(descriptor) {
395
+ const rootsOrder = Object.keys(descriptor.roots ?? {});
396
+ const modelsOrder = Object.keys(descriptor.models ?? {});
397
+ for (const modelName of modelsOrder) {
398
+ const model = descriptor.models?.[modelName];
399
+ if (!model || typeof model !== "object")
400
+ continue;
401
+ const keys = Object.keys(model.properties ?? {});
402
+ for (const key of keys) {
403
+ model.properties[key] = applyFieldPropertyOrder(model.properties[key]);
404
+ }
405
+ model.propertyOrder = normalizePropertyOrder(keys, model.propertyOrder);
406
+ }
407
+ for (const rootName of rootsOrder) {
408
+ const rootDescriptor = descriptor.roots?.[rootName];
409
+ if (!rootDescriptor)
410
+ continue;
411
+ descriptor.roots[rootName] = applyFieldPropertyOrder(rootDescriptor);
412
+ }
413
+ descriptor.metadata = {
414
+ ...(descriptor.metadata ?? {}),
415
+ ordering: {
416
+ ...(descriptor.metadata?.ordering ?? {}),
417
+ roots: rootsOrder,
418
+ models: modelsOrder,
419
+ },
420
+ };
421
+ return descriptor;
422
+ }
192
423
  function collectModels(sourceFiles, modelNames, modelMap, warnings) {
193
424
  const handleShape = (name, type, ctx) => {
194
425
  if (modelMap[name])
@@ -290,15 +521,15 @@ function buildDescriptorAlt(resolved) {
290
521
  const referencedModelNames = new Set();
291
522
  collectReferencedModelNames(rootType, referencedModelNames, new Set(), "root");
292
523
  const referencedCustomTypeNames = new Set(Array.from(referencedModelNames).filter((n) => customTypeNames.has(n)));
293
- const referencedCustomModelNames = new Set(Array.from(referencedCustomTypeNames).filter((n) => customModelTypeNames.has(n)));
294
- const resolvedCustomTypeNames = resolveCustomTypeDependencies(referencedCustomModelNames);
524
+ const resolvedCustomTypeNames = resolveCustomTypeDependencies(referencedCustomTypeNames);
525
+ const resolvedCustomModelNames = new Set(Array.from(resolvedCustomTypeNames).filter((n) => customModelTypeNames.has(n)));
295
526
  const modelNames = new Set([
296
527
  ...Array.from(referencedModelNames).filter((n) => exportedModelNames.has(n)),
297
- ...Array.from(resolvedCustomTypeNames),
528
+ ...Array.from(resolvedCustomModelNames),
298
529
  ]);
299
530
  const modelMap = {};
300
531
  collectModels(sourceFiles, modelNames, modelMap, warnings);
301
- resolvedCustomTypeNames.forEach((name) => {
532
+ resolvedCustomModelNames.forEach((name) => {
302
533
  if (modelMap[name]) {
303
534
  return;
304
535
  }
@@ -335,7 +566,7 @@ function buildDescriptorAlt(resolved) {
335
566
  warnings.forEach((w) => console.warn(w));
336
567
  const locales = invocation.locales;
337
568
  const defaultLocale = invocation.defaultLocale;
338
- return {
569
+ const descriptor = {
339
570
  models: modelMap,
340
571
  roots,
341
572
  metadata: locales || defaultLocale
@@ -345,5 +576,6 @@ function buildDescriptorAlt(resolved) {
345
576
  }
346
577
  : undefined,
347
578
  };
579
+ return applyDescriptorOrderingMetadata(descriptor);
348
580
  }
349
581
  export { buildDescriptorAlt };
@@ -0,0 +1,220 @@
1
+ function isRecord(value) {
2
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3
+ }
4
+ function normalizeText(value) {
5
+ if (typeof value !== "string")
6
+ return undefined;
7
+ const trimmed = value.trim();
8
+ return trimmed.length > 0 ? trimmed : undefined;
9
+ }
10
+ function uniqueNonEmpty(values) {
11
+ const result = [];
12
+ for (const value of values) {
13
+ const trimmed = normalizeText(value);
14
+ if (!trimmed || result.includes(trimmed))
15
+ continue;
16
+ result.push(trimmed);
17
+ }
18
+ return result;
19
+ }
20
+ function isLocalizedLike(value) {
21
+ if (!isRecord(value))
22
+ return false;
23
+ return "locales" in value && isRecord(value.locales);
24
+ }
25
+ function localizedValueForLocale(locales, locale) {
26
+ const entry = locales[locale];
27
+ return normalizeText(entry);
28
+ }
29
+ export function resolveLocalized(value, options = {}) {
30
+ if (value == null)
31
+ return undefined;
32
+ if (typeof value === "string") {
33
+ return normalizeText(value);
34
+ }
35
+ if (!isLocalizedLike(value)) {
36
+ return undefined;
37
+ }
38
+ const locales = value.locales;
39
+ const localeKeys = Object.keys(locales);
40
+ const preferredLocales = uniqueNonEmpty([
41
+ options.locale,
42
+ typeof value.defaultLocale === "string" ? value.defaultLocale : undefined,
43
+ options.defaultLocale,
44
+ options.fallbackLocale,
45
+ ...localeKeys,
46
+ ]);
47
+ for (const locale of preferredLocales) {
48
+ const resolved = localizedValueForLocale(locales, locale);
49
+ if (resolved)
50
+ return resolved;
51
+ }
52
+ return undefined;
53
+ }
54
+ function resolveKeywords(value, options) {
55
+ if (Array.isArray(value)) {
56
+ const list = value.map((entry) => normalizeText(entry)).filter(Boolean);
57
+ return list.length ? list : undefined;
58
+ }
59
+ const localized = resolveLocalized(value, options);
60
+ if (!localized)
61
+ return undefined;
62
+ const parts = localized
63
+ .split(/[;,]/g)
64
+ .map((entry) => entry.trim())
65
+ .filter((entry) => entry.length > 0);
66
+ if (parts.length > 0) {
67
+ return parts;
68
+ }
69
+ return [localized];
70
+ }
71
+ function resolveImageUrl(image) {
72
+ if (typeof image === "string") {
73
+ return normalizeText(image);
74
+ }
75
+ if (!isRecord(image))
76
+ return undefined;
77
+ return normalizeText(image.url);
78
+ }
79
+ function normalizeRobots(robots) {
80
+ if (typeof robots === "boolean") {
81
+ return { index: robots, follow: robots };
82
+ }
83
+ if (!isRecord(robots))
84
+ return undefined;
85
+ const resolved = {};
86
+ if (typeof robots.index === "boolean")
87
+ resolved.index = robots.index;
88
+ if (typeof robots.follow === "boolean")
89
+ resolved.follow = robots.follow;
90
+ if (typeof robots.nocache === "boolean")
91
+ resolved.nocache = robots.nocache;
92
+ return Object.keys(resolved).length ? resolved : undefined;
93
+ }
94
+ function hasAnyKeys(value) {
95
+ return Object.keys(value).length > 0;
96
+ }
97
+ export function toOpenGraph(value, options = {}) {
98
+ if (!isRecord(value))
99
+ return undefined;
100
+ const openGraph = {};
101
+ const type = normalizeText(value.type);
102
+ if (type)
103
+ openGraph.type = type;
104
+ const url = normalizeText(value.url);
105
+ if (url)
106
+ openGraph.url = url;
107
+ const siteName = normalizeText(value.siteName);
108
+ if (siteName)
109
+ openGraph.siteName = siteName;
110
+ const title = resolveLocalized(value.title, options);
111
+ if (title)
112
+ openGraph.title = title;
113
+ const description = resolveLocalized(value.description, options);
114
+ if (description)
115
+ openGraph.description = description;
116
+ if (Array.isArray(value.images)) {
117
+ const images = value.images
118
+ .map((image) => resolveImageUrl(image))
119
+ .filter((entry) => typeof entry === "string" && entry.length > 0);
120
+ if (images.length) {
121
+ openGraph.images = images;
122
+ }
123
+ }
124
+ const locale = normalizeText(value.locale);
125
+ if (locale)
126
+ openGraph.locale = locale;
127
+ if (Array.isArray(value.alternateLocale)) {
128
+ const alternateLocale = value.alternateLocale
129
+ .map((entry) => normalizeText(entry))
130
+ .filter((entry) => Boolean(entry));
131
+ if (alternateLocale.length) {
132
+ openGraph.alternateLocale = alternateLocale;
133
+ }
134
+ }
135
+ return hasAnyKeys(openGraph) ? openGraph : undefined;
136
+ }
137
+ export function toTwitter(value, options = {}) {
138
+ if (!isRecord(value))
139
+ return undefined;
140
+ const twitter = {};
141
+ const card = normalizeText(value.card);
142
+ if (card)
143
+ twitter.card = card;
144
+ const site = normalizeText(value.site);
145
+ if (site)
146
+ twitter.site = site;
147
+ const creator = normalizeText(value.creator);
148
+ if (creator)
149
+ twitter.creator = creator;
150
+ const title = resolveLocalized(value.title, options);
151
+ if (title)
152
+ twitter.title = title;
153
+ const description = resolveLocalized(value.description, options);
154
+ if (description)
155
+ twitter.description = description;
156
+ if (Array.isArray(value.images)) {
157
+ const images = value.images
158
+ .map((image) => resolveImageUrl(image))
159
+ .filter((entry) => typeof entry === "string" && entry.length > 0);
160
+ if (images.length) {
161
+ twitter.images = images;
162
+ }
163
+ }
164
+ return hasAnyKeys(twitter) ? twitter : undefined;
165
+ }
166
+ function resolveAlternates(seo) {
167
+ const alternates = {};
168
+ const canonical = normalizeText(seo.alternates?.canonical) ?? normalizeText(seo.canonical);
169
+ if (canonical) {
170
+ alternates.canonical = canonical;
171
+ }
172
+ if (isRecord(seo.alternates?.languages)) {
173
+ const languages = {};
174
+ for (const [locale, rawValue] of Object.entries(seo.alternates.languages)) {
175
+ const normalizedLocale = normalizeText(locale);
176
+ const normalizedUrl = normalizeText(rawValue);
177
+ if (!normalizedLocale || !normalizedUrl)
178
+ continue;
179
+ languages[normalizedLocale] = normalizedUrl;
180
+ }
181
+ if (Object.keys(languages).length) {
182
+ alternates.languages = languages;
183
+ }
184
+ }
185
+ return hasAnyKeys(alternates) ? alternates : undefined;
186
+ }
187
+ /**
188
+ * Maps a CMS0 `Seo` object to a Next.js-compatible metadata shape.
189
+ *
190
+ * The return type is generic so consumers can pass Next's `Metadata` type:
191
+ * `toNextMetadata<Metadata>(seo, { locale })`.
192
+ */
193
+ export function toNextMetadata(seo, options = {}) {
194
+ if (!isRecord(seo)) {
195
+ return {};
196
+ }
197
+ const metadata = {};
198
+ const title = resolveLocalized(seo.title, options);
199
+ if (title)
200
+ metadata.title = title;
201
+ const description = resolveLocalized(seo.description, options);
202
+ if (description)
203
+ metadata.description = description;
204
+ const keywords = resolveKeywords(seo.keywords, options);
205
+ if (keywords?.length)
206
+ metadata.keywords = keywords;
207
+ const robots = normalizeRobots(seo.robots);
208
+ if (robots)
209
+ metadata.robots = robots;
210
+ const alternates = resolveAlternates(seo);
211
+ if (alternates)
212
+ metadata.alternates = alternates;
213
+ const openGraph = toOpenGraph(seo.openGraph, options);
214
+ if (openGraph)
215
+ metadata.openGraph = openGraph;
216
+ const twitter = toTwitter(seo.twitter, options);
217
+ if (twitter)
218
+ metadata.twitter = twitter;
219
+ return metadata;
220
+ }
@@ -28,4 +28,42 @@ export type Localized<T> = {
28
28
  };
29
29
  export type LocalizedString = Localized<string>;
30
30
  export type LocalizedRichText = Localized<RichText>;
31
+ export type SeoRobots = boolean | {
32
+ index?: boolean;
33
+ follow?: boolean;
34
+ nocache?: boolean;
35
+ };
36
+ export type SeoAlternates = {
37
+ canonical?: string;
38
+ languages?: Record<string, string>;
39
+ };
40
+ export type SeoOpenGraph = {
41
+ type?: "website" | "article" | "profile";
42
+ url?: string;
43
+ siteName?: string;
44
+ title?: LocalizedString | string;
45
+ description?: LocalizedString | string;
46
+ images?: Array<Image | string>;
47
+ locale?: string;
48
+ alternateLocale?: string[];
49
+ };
50
+ export type SeoTwitter = {
51
+ card?: "summary" | "summary_large_image" | "app" | "player";
52
+ site?: string;
53
+ creator?: string;
54
+ title?: LocalizedString | string;
55
+ description?: LocalizedString | string;
56
+ images?: Array<Image | string>;
57
+ };
58
+ export type Seo = {
59
+ title?: LocalizedString | string;
60
+ description?: LocalizedString | string;
61
+ keywords?: LocalizedString | string[];
62
+ canonical?: string;
63
+ robots?: SeoRobots;
64
+ alternates?: SeoAlternates;
65
+ openGraph?: SeoOpenGraph;
66
+ twitter?: SeoTwitter;
67
+ jsonLd?: Record<string, unknown>[];
68
+ };
31
69
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/custom-types/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG,QAAQ,CAAC;AAE5B,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;AAEhD,MAAM,MAAM,iBAAiB,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/custom-types/index.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { ModelDescriptor, PrimitiveType } from "@cms0/shared";
1
+ import { type FieldDescriptor, type ModelDescriptor, type PrimitiveType } from "@cms0/shared";
2
2
  export declare const customModelDescriptors: {
3
3
  readonly File: ModelDescriptor;
4
4
  readonly Image: ModelDescriptor;
@@ -26,6 +26,15 @@ export declare const customInlineDescriptors: {
26
26
  nullable?: boolean;
27
27
  customType?: string;
28
28
  };
29
+ readonly Seo: {
30
+ kind?: "object";
31
+ type: "object";
32
+ properties: Record<string, FieldDescriptor>;
33
+ propertyOrder?: string[];
34
+ optional?: boolean;
35
+ nullable?: boolean;
36
+ customType?: string;
37
+ };
29
38
  };
30
39
  export type CustomInlineTypeName = keyof typeof customInlineDescriptors;
31
40
  export type CustomInlineTypeMetadata = {
@@ -56,6 +65,15 @@ export declare const customTypeDescriptors: {
56
65
  nullable?: boolean;
57
66
  customType?: string;
58
67
  };
68
+ readonly Seo: {
69
+ kind?: "object";
70
+ type: "object";
71
+ properties: Record<string, FieldDescriptor>;
72
+ propertyOrder?: string[];
73
+ optional?: boolean;
74
+ nullable?: boolean;
75
+ customType?: string;
76
+ };
59
77
  readonly File: ModelDescriptor;
60
78
  readonly Image: ModelDescriptor;
61
79
  readonly Video: ModelDescriptor;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/custom-types/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AA6DpF,eAAO,MAAM,sBAAsB;;;;CAIzB,CAAC;AAEX,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;CAI1B,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,uBAAuB,CAAC;AAExE,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,CAAC,EAAE;QAEV,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KACnC,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAC3C,oBAAoB,EACpB,wBAAwB,CASzB,CAAC;AAEF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;CAGxB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,qBAAqB,CAAC;AAEhE,eAAO,MAAM,eAAe,aAAsD,CAAC;AACnF,eAAO,MAAM,oBAAoB,aAEhC,CAAC;AACF,eAAO,MAAM,qBAAqB,aAEjC,CAAC;AAEF,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,GACX,wBAAwB,GAAG,SAAS,CAEtC;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAwBlF"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/custom-types/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,aAAa,EACnB,MAAM,cAAc,CAAC;AA6StB,eAAO,MAAM,sBAAsB;;;;CAIzB,CAAC;AAEX,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAK1B,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,uBAAuB,CAAC;AAExE,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,CAAC,EAAE;QAEV,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KACnC,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAC3C,oBAAoB,EACpB,wBAAwB,CAUzB,CAAC;AAEF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAGxB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,qBAAqB,CAAC;AAEhE,eAAO,MAAM,eAAe,aAAsD,CAAC;AACnF,eAAO,MAAM,oBAAoB,aAEhC,CAAC;AACF,eAAO,MAAM,qBAAqB,aAEjC,CAAC;AAEF,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,GACX,wBAAwB,GAAG,SAAS,CAEtC;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAgClF"}
@@ -1,7 +1,7 @@
1
1
  import { Cms0APIConfig } from "@cms0/cms0/config";
2
2
  import { schemaDescriptor } from "@cms0/cms0/schema-descriptors";
3
3
  import { FullDescriptor } from "@cms0/shared";
4
- import type { LocalizedRichText as CmsLocalizedRichText, LocalizedString as CmsLocalizedString, RichText as CmsRichText } from "./custom-types/index.js";
4
+ import type { LocalizedRichText as CmsLocalizedRichText, Seo as CmsSeo, LocalizedString as CmsLocalizedString, RichText as CmsRichText } from "./custom-types/index.js";
5
5
  type QueryValue = string | number | boolean | null | undefined;
6
6
  type QueryParam = QueryValue | QueryValue[];
7
7
  export type CmsQuery = Record<string, QueryParam>;
@@ -94,7 +94,9 @@ export interface Cms0Options {
94
94
  includeId?: boolean;
95
95
  }
96
96
  type PrimitiveValueFromDescriptor<TypeName extends string> = TypeName extends "string" ? string : TypeName extends "number" ? number : TypeName extends "boolean" ? boolean : any;
97
- type CustomTypeValueFromDescriptor<TypeName extends string> = TypeName extends "RichText" ? CmsRichText : TypeName extends "LocalizedString" ? CmsLocalizedString : TypeName extends "LocalizedRichText" ? CmsLocalizedRichText : never;
97
+ type EnumValueTypeFromDescriptor<TypeName extends string> = TypeName extends "string" ? string : TypeName extends "number" ? number : TypeName extends "boolean" ? boolean : never;
98
+ type EnumLiteralFromDescriptor<ValueType extends string, Values> = Values extends readonly (infer Literal)[] ? Literal extends EnumValueTypeFromDescriptor<ValueType> ? Literal : EnumValueTypeFromDescriptor<ValueType> : EnumValueTypeFromDescriptor<ValueType>;
99
+ type CustomTypeValueFromDescriptor<TypeName extends string> = TypeName extends "RichText" ? CmsRichText : TypeName extends "LocalizedString" ? CmsLocalizedString : TypeName extends "LocalizedRichText" ? CmsLocalizedRichText : TypeName extends "Seo" ? CmsSeo : never;
98
100
  type ApplyDescriptorFlags<Descriptor, Value> = Descriptor extends {
99
101
  optional: true;
100
102
  nullable: true;
@@ -106,6 +108,7 @@ type ApplyDescriptorFlags<Descriptor, Value> = Descriptor extends {
106
108
  type InferDescriptorObject<Properties extends Record<string, any>, ModelMap extends Record<string, any>> = {
107
109
  [Key in keyof Properties]: InferDescriptorField<Properties[Key], ModelMap>;
108
110
  };
111
+ type InferUnionBranches<Branches, ModelMap extends Record<string, any>> = Branches extends readonly (infer Branch)[] ? InferDescriptorField<Branch, ModelMap> : any;
109
112
  type InferDescriptorField<Descriptor, ModelMap extends Record<string, any>> = ApplyDescriptorFlags<Descriptor, Descriptor extends {
110
113
  customType: infer CustomType extends string;
111
114
  } ? CustomTypeValueFromDescriptor<CustomType> extends never ? Descriptor extends {
@@ -115,6 +118,13 @@ type InferDescriptorField<Descriptor, ModelMap extends Record<string, any>> = Ap
115
118
  kind: "primitive";
116
119
  type: infer Primitive extends string;
117
120
  } ? PrimitiveValueFromDescriptor<Primitive> : Descriptor extends {
121
+ kind: "enum";
122
+ valueType: infer ValueType extends string;
123
+ values: infer Values;
124
+ } ? EnumLiteralFromDescriptor<ValueType, Values> : Descriptor extends {
125
+ kind: "union";
126
+ anyOf: infer Branches;
127
+ } ? InferUnionBranches<Branches, ModelMap> : Descriptor extends {
118
128
  type: "array";
119
129
  items: infer Items;
120
130
  } ? Array<InferDescriptorField<Items, ModelMap>> : Descriptor extends {
@@ -129,6 +139,13 @@ type InferDescriptorField<Descriptor, ModelMap extends Record<string, any>> = Ap
129
139
  kind: "primitive";
130
140
  type: infer Primitive extends string;
131
141
  } ? PrimitiveValueFromDescriptor<Primitive> : Descriptor extends {
142
+ kind: "enum";
143
+ valueType: infer ValueType extends string;
144
+ values: infer Values;
145
+ } ? EnumLiteralFromDescriptor<ValueType, Values> : Descriptor extends {
146
+ kind: "union";
147
+ anyOf: infer Branches;
148
+ } ? InferUnionBranches<Branches, ModelMap> : Descriptor extends {
132
149
  type: "array";
133
150
  items: infer Items;
134
151
  } ? Array<InferDescriptorField<Items, ModelMap>> : Descriptor extends {
@@ -174,5 +191,6 @@ export declare function cms0<Roots = GeneratedRoots, Models extends Record<strin
174
191
  includeId?: false | undefined;
175
192
  }): CmsInstance<Roots, Models, false>;
176
193
  export declare function cms0<Roots = GeneratedRoots, Models extends Record<string, any> = GeneratedModels>(config: Cms0Options): CmsInstance<Roots, Models, boolean>;
177
- export {};
194
+ export { resolveLocalized, toNextMetadata, toOpenGraph, toTwitter, } from "./seo.js";
195
+ export type { ResolveLocalizedOptions, ToNextMetadataOptions, } from "./seo.js";
178
196
  //# sourceMappingURL=index.d.ts.map