+25
packages/emitter/lib/decorators.tsp
+25
packages/emitter/lib/decorators.tsp
···
163
163
extern dec errors(target: unknown, ...errors: unknown[]);
164
164
165
165
/**
166
+
* Specifies a default value for a scalar or union definition.
167
+
* Only valid on standalone scalar or union defs (not @inline).
168
+
* The value must match the underlying type (string, integer, or boolean).
169
+
* For unions with token refs, you can pass a model reference directly.
170
+
*
171
+
* @param value - The default value (literal or model reference for tokens)
172
+
*
173
+
* @example Scalar with default
174
+
* ```typespec
175
+
* @default("standard")
176
+
* scalar Mode extends string;
177
+
* ```
178
+
*
179
+
* @example Union with token default
180
+
* ```typespec
181
+
* @default(Inperson)
182
+
* union EventMode { Hybrid, Inperson, Virtual, string }
183
+
*
184
+
* @token
185
+
* model Inperson {}
186
+
* ```
187
+
*/
188
+
extern dec `default`(target: unknown, value: unknown);
189
+
190
+
/**
166
191
* Marks a namespace as external, preventing it from emitting JSON output.
167
192
* This decorator can only be applied to namespaces.
168
193
* Useful for importing definitions from other lexicons without re-emitting them.
+17
packages/emitter/src/decorators.ts
+17
packages/emitter/src/decorators.ts
···
25
25
const maxBytesKey = Symbol("maxBytes");
26
26
const minBytesKey = Symbol("minBytes");
27
27
const externalKey = Symbol("external");
28
+
const defaultKey = Symbol("default");
28
29
29
30
/**
30
31
* @maxBytes decorator for maximum length of bytes type
···
294
295
295
296
export function isReadOnly(program: Program, target: Type): boolean {
296
297
return program.stateSet(readOnlyKey).has(target);
298
+
}
299
+
300
+
/**
301
+
* @default decorator for setting default values on scalars and unions
302
+
* The value can be a literal (string, number, boolean) or a model reference for tokens
303
+
*/
304
+
export function $default(context: DecoratorContext, target: Type, value: any) {
305
+
// Just store the raw value - let the emitter handle unwrapping and validation
306
+
context.program.stateMap(defaultKey).set(target, value);
307
+
}
308
+
309
+
export function getDefault(
310
+
program: Program,
311
+
target: Type,
312
+
): any | undefined {
313
+
return program.stateMap(defaultKey).get(target);
297
314
}
298
315
299
316
/**
+235
-16
packages/emitter/src/emitter.ts
+235
-16
packages/emitter/src/emitter.ts
···
68
68
getMaxBytes,
69
69
getMinBytes,
70
70
isExternal,
71
+
getDefault,
71
72
} from "./decorators.js";
72
73
73
74
export interface EmitterOptions {
···
97
98
private program: Program,
98
99
private options: EmitterOptions,
99
100
) {}
101
+
102
+
/**
103
+
* Process the raw default value from the decorator, unwrapping TypeSpec value objects
104
+
* and returning either a primitive (string, number, boolean) or a Type (for model references)
105
+
*/
106
+
private processDefaultValue(rawValue: any): string | number | boolean | Type | undefined {
107
+
if (rawValue === undefined) return undefined;
108
+
109
+
// TypeSpec may wrap values - check if this is a value object first
110
+
if (rawValue && typeof rawValue === 'object' && rawValue.valueKind) {
111
+
if (rawValue.valueKind === "StringValue") {
112
+
return rawValue.value;
113
+
} else if (rawValue.valueKind === "NumericValue" || rawValue.valueKind === "NumberValue") {
114
+
return rawValue.value;
115
+
} else if (rawValue.valueKind === "BooleanValue") {
116
+
return rawValue.value;
117
+
}
118
+
return undefined; // Unsupported valueKind
119
+
}
120
+
121
+
// Check if it's a Type object (Model, String, Number, Boolean literals)
122
+
if (rawValue && typeof rawValue === 'object' && rawValue.kind) {
123
+
if (rawValue.kind === "String") {
124
+
return (rawValue as StringLiteral).value;
125
+
} else if (rawValue.kind === "Number") {
126
+
return (rawValue as NumericLiteral).value;
127
+
} else if (rawValue.kind === "Boolean") {
128
+
return (rawValue as BooleanLiteral).value;
129
+
} else if (rawValue.kind === "Model") {
130
+
// Return the model itself for token references
131
+
return rawValue as Model;
132
+
}
133
+
return undefined; // Unsupported kind
134
+
}
135
+
136
+
// Direct primitive value
137
+
if (typeof rawValue === 'string' || typeof rawValue === 'number' || typeof rawValue === 'boolean') {
138
+
return rawValue;
139
+
}
140
+
141
+
return undefined;
142
+
}
100
143
101
144
async emit() {
102
145
const globalNs = this.program.getGlobalNamespaceType();
···
356
399
}
357
400
358
401
private addScalarToDefs(lexicon: LexiconDoc, scalar: Scalar) {
402
+
// Only skip if the scalar itself is in TypeSpec namespace (built-in scalars)
359
403
if (scalar.namespace?.name === "TypeSpec") return;
360
-
if (scalar.baseScalar?.namespace?.name === "TypeSpec") return;
361
404
362
405
// Skip @inline scalars - they should be inlined, not defined separately
363
406
if (isInline(this.program, scalar)) {
···
368
411
const scalarDef = this.scalarToLexiconPrimitive(scalar, undefined);
369
412
if (scalarDef) {
370
413
const description = getDoc(this.program, scalar);
371
-
lexicon.defs[defName] = { ...scalarDef, description } as LexUserType;
414
+
415
+
// Apply @default decorator if present
416
+
const rawDefault = getDefault(this.program, scalar);
417
+
const defaultValue = this.processDefaultValue(rawDefault);
418
+
let defWithDefault: any = { ...scalarDef };
419
+
420
+
if (defaultValue !== undefined) {
421
+
// Check if it's a Type (model reference for tokens)
422
+
if (typeof defaultValue === 'object' && 'kind' in defaultValue) {
423
+
// For model references, we need to resolve to NSID
424
+
// This shouldn't happen for scalars, only unions support token refs
425
+
this.program.reportDiagnostic({
426
+
code: "invalid-default-on-scalar",
427
+
severity: "error",
428
+
message: "@default on scalars must be a literal value (string, number, or boolean), not a model reference",
429
+
target: scalar,
430
+
});
431
+
} else {
432
+
// Validate that the default value matches the type
433
+
this.assertValidValueForType(scalarDef.type, defaultValue, scalar);
434
+
defWithDefault = { ...defWithDefault, default: defaultValue };
435
+
}
436
+
}
437
+
438
+
// Apply integer constraints for standalone scalar defs
439
+
if (scalarDef.type === "integer") {
440
+
const minValue = getMinValue(this.program, scalar);
441
+
if (minValue !== undefined) {
442
+
(defWithDefault as any).minimum = minValue;
443
+
}
444
+
const maxValue = getMaxValue(this.program, scalar);
445
+
if (maxValue !== undefined) {
446
+
(defWithDefault as any).maximum = maxValue;
447
+
}
448
+
}
449
+
450
+
lexicon.defs[defName] = { ...defWithDefault, description } as LexUserType;
372
451
}
373
452
}
374
453
···
391
470
if (unionDef.type === "string" && (unionDef.knownValues || unionDef.enum)) {
392
471
const defName = name.charAt(0).toLowerCase() + name.slice(1);
393
472
const description = getDoc(this.program, union);
394
-
lexicon.defs[defName] = { ...unionDef, description };
473
+
474
+
// Apply @default decorator if present
475
+
const rawDefault = getDefault(this.program, union);
476
+
const defaultValue = this.processDefaultValue(rawDefault);
477
+
let defWithDefault: any = { ...unionDef };
478
+
479
+
if (defaultValue !== undefined) {
480
+
// Check if it's a Type (model reference for tokens)
481
+
if (typeof defaultValue === 'object' && 'kind' in defaultValue) {
482
+
// Resolve the model reference to its NSID
483
+
const tokenModel = defaultValue as Model;
484
+
const tokenRef = this.getModelReference(tokenModel, true); // fullyQualified=true
485
+
if (tokenRef) {
486
+
defWithDefault = { ...defWithDefault, default: tokenRef };
487
+
} else {
488
+
this.program.reportDiagnostic({
489
+
code: "invalid-default-token",
490
+
severity: "error",
491
+
message: "@default value must be a valid token model reference",
492
+
target: union,
493
+
});
494
+
}
495
+
} else {
496
+
// Literal value - validate it matches the union type
497
+
if (typeof defaultValue !== "string") {
498
+
this.program.reportDiagnostic({
499
+
code: "invalid-default-value-type",
500
+
severity: "error",
501
+
message: `Default value type mismatch: expected string, got ${typeof defaultValue}`,
502
+
target: union,
503
+
});
504
+
} else {
505
+
defWithDefault = { ...defWithDefault, default: defaultValue };
506
+
}
507
+
}
508
+
}
509
+
510
+
lexicon.defs[defName] = { ...defWithDefault, description };
395
511
} else if (unionDef.type === "union") {
396
512
this.program.reportDiagnostic({
397
513
code: "union-refs-not-allowed-as-def",
···
401
517
`Use @inline to inline them at usage sites, use @token models for known values, or use string literals.`,
402
518
target: union,
403
519
});
520
+
} else if (unionDef.type === "integer" && (unionDef as any).enum) {
521
+
// Integer enums can also be defs
522
+
const defName = name.charAt(0).toLowerCase() + name.slice(1);
523
+
const description = getDoc(this.program, union);
524
+
525
+
// Apply @default decorator if present
526
+
const rawDefault = getDefault(this.program, union);
527
+
const defaultValue = this.processDefaultValue(rawDefault);
528
+
let defWithDefault = { ...unionDef };
529
+
530
+
if (defaultValue !== undefined) {
531
+
if (typeof defaultValue === "number") {
532
+
defWithDefault = { ...defWithDefault, default: defaultValue };
533
+
} else {
534
+
this.program.reportDiagnostic({
535
+
code: "invalid-default-value-type",
536
+
severity: "error",
537
+
message: `Default value type mismatch: expected integer, got ${typeof defaultValue}`,
538
+
target: union,
539
+
});
540
+
}
541
+
}
542
+
543
+
lexicon.defs[defName] = { ...defWithDefault, description };
404
544
}
405
545
}
406
546
···
1158
1298
prop?: ModelProperty,
1159
1299
propDesc?: string,
1160
1300
): LexObjectProperty | null {
1301
+
// Check if this scalar should be referenced instead of inlined
1302
+
const scalarRef = this.getScalarReference(scalar);
1303
+
if (scalarRef) {
1304
+
// Check if property has a default value that would conflict with the scalar's @default
1305
+
if (prop?.defaultValue !== undefined) {
1306
+
const scalarDefaultRaw = getDefault(this.program, scalar);
1307
+
const scalarDefault = this.processDefaultValue(scalarDefaultRaw);
1308
+
const propDefault = serializeValueAsJson(this.program, prop.defaultValue, prop);
1309
+
1310
+
// If the scalar has a different default, or if the property has a default but the scalar doesn't, error
1311
+
if (scalarDefault !== propDefault) {
1312
+
this.program.reportDiagnostic({
1313
+
code: "conflicting-defaults",
1314
+
severity: "error",
1315
+
message: scalarDefault !== undefined
1316
+
? `Property default value conflicts with scalar's @default decorator. The scalar "${scalar.name}" has @default(${JSON.stringify(scalarDefault)}) but property has default value ${JSON.stringify(propDefault)}. Either remove the property default, mark the scalar @inline, or make the defaults match.`
1317
+
: `Property has a default value but the referenced scalar "${scalar.name}" does not. Either add @default to the scalar, mark it @inline to allow property-level defaults, or remove the property default.`,
1318
+
target: prop,
1319
+
});
1320
+
}
1321
+
}
1322
+
1323
+
return { type: "ref" as const, ref: scalarRef, description: propDesc };
1324
+
}
1325
+
1326
+
// Inline the scalar
1161
1327
const primitive = this.scalarToLexiconPrimitive(scalar, prop);
1162
1328
if (!primitive) return null;
1163
1329
···
1246
1412
if (!isDefining) {
1247
1413
const unionRef = this.getUnionReference(unionType);
1248
1414
if (unionRef) {
1415
+
// Check if property has a default value that would conflict with the union's @default
1416
+
if (prop?.defaultValue !== undefined) {
1417
+
const unionDefaultRaw = getDefault(this.program, unionType);
1418
+
const unionDefault = this.processDefaultValue(unionDefaultRaw);
1419
+
const propDefault = serializeValueAsJson(this.program, prop.defaultValue, prop);
1420
+
1421
+
// For union defaults that are model references, we need to resolve them for comparison
1422
+
let resolvedUnionDefault: string | number | boolean | undefined = unionDefault as any;
1423
+
if (unionDefault && typeof unionDefault === 'object' && 'kind' in unionDefault && unionDefault.kind === 'Model') {
1424
+
const ref = this.getModelReference(unionDefault as Model, true);
1425
+
resolvedUnionDefault = ref || undefined;
1426
+
}
1427
+
1428
+
// If the union has a different default, or if the property has a default but the union doesn't, error
1429
+
if (resolvedUnionDefault !== propDefault) {
1430
+
this.program.reportDiagnostic({
1431
+
code: "conflicting-defaults",
1432
+
severity: "error",
1433
+
message: unionDefault !== undefined
1434
+
? `Property default value conflicts with union's @default decorator. The union "${unionType.name}" has @default(${JSON.stringify(resolvedUnionDefault)}) but property has default value ${JSON.stringify(propDefault)}. Either remove the property default, mark the union @inline, or make the defaults match.`
1435
+
: `Property has a default value but the referenced union "${unionType.name}" does not. Either add @default to the union, mark it @inline to allow property-level defaults, or remove the property default.`,
1436
+
target: prop,
1437
+
});
1438
+
}
1439
+
}
1440
+
1249
1441
return { type: "ref" as const, ref: unionRef, description: propDesc };
1250
1442
}
1251
1443
}
···
1271
1463
// Check if this scalar (or its base) is bytes type
1272
1464
if (this.isScalarOfType(scalar, "bytes")) {
1273
1465
const byteDef: LexBytes = { type: "bytes" };
1274
-
const target = prop || scalar;
1275
1466
1276
-
const minLength = getMinBytes(this.program, target);
1467
+
// Check scalar first for its own constraints, then property overrides
1468
+
const minLength = getMinBytes(this.program, scalar) ?? (prop ? getMinBytes(this.program, prop) : undefined);
1277
1469
if (minLength !== undefined) {
1278
1470
byteDef.minLength = minLength;
1279
1471
}
1280
1472
1281
-
const maxLength = getMaxBytes(this.program, target);
1473
+
const maxLength = getMaxBytes(this.program, scalar) ?? (prop ? getMaxBytes(this.program, prop) : undefined);
1282
1474
if (maxLength !== undefined) {
1283
1475
byteDef.maxLength = maxLength;
1284
1476
}
···
1310
1502
1311
1503
// Apply string constraints
1312
1504
if (primitive.type === "string") {
1313
-
const target = prop || scalar;
1314
-
const maxLength = getMaxLength(this.program, target);
1505
+
// Check scalar first for its own constraints, then property overrides
1506
+
const maxLength = getMaxLength(this.program, scalar) ?? (prop ? getMaxLength(this.program, prop) : undefined);
1315
1507
if (maxLength !== undefined) {
1316
1508
primitive.maxLength = maxLength;
1317
1509
}
1318
-
const minLength = getMinLength(this.program, target);
1510
+
const minLength = getMinLength(this.program, scalar) ?? (prop ? getMinLength(this.program, prop) : undefined);
1319
1511
if (minLength !== undefined) {
1320
1512
primitive.minLength = minLength;
1321
1513
}
1322
-
const maxGraphemes = getMaxGraphemes(this.program, target);
1514
+
const maxGraphemes = getMaxGraphemes(this.program, scalar) ?? (prop ? getMaxGraphemes(this.program, prop) : undefined);
1323
1515
if (maxGraphemes !== undefined) {
1324
1516
primitive.maxGraphemes = maxGraphemes;
1325
1517
}
1326
-
const minGraphemes = getMinGraphemes(this.program, target);
1518
+
const minGraphemes = getMinGraphemes(this.program, scalar) ?? (prop ? getMinGraphemes(this.program, prop) : undefined);
1327
1519
if (minGraphemes !== undefined) {
1328
1520
primitive.minGraphemes = minGraphemes;
1329
1521
}
1330
1522
}
1331
1523
1332
1524
// Apply numeric constraints
1333
-
if (prop && primitive.type === "integer") {
1334
-
const minValue = getMinValue(this.program, prop);
1525
+
if (primitive.type === "integer") {
1526
+
// Check scalar first for its own constraints, then property overrides
1527
+
const minValue = getMinValue(this.program, scalar) ?? (prop ? getMinValue(this.program, prop) : undefined);
1335
1528
if (minValue !== undefined) {
1336
1529
primitive.minimum = minValue;
1337
1530
}
1338
-
const maxValue = getMaxValue(this.program, prop);
1531
+
const maxValue = getMaxValue(this.program, scalar) ?? (prop ? getMaxValue(this.program, prop) : undefined);
1339
1532
if (maxValue !== undefined) {
1340
1533
primitive.maximum = maxValue;
1341
1534
}
···
1431
1624
private assertValidValueForType(
1432
1625
primitiveType: string,
1433
1626
value: unknown,
1434
-
prop: ModelProperty,
1627
+
target: ModelProperty | Scalar | Union,
1435
1628
): void {
1436
1629
const valid =
1437
1630
(primitiveType === "boolean" && typeof value === "boolean") ||
···
1442
1635
code: "invalid-default-value-type",
1443
1636
severity: "error",
1444
1637
message: `Default value type mismatch: expected ${primitiveType}, got ${typeof value}`,
1445
-
target: prop,
1638
+
target: target,
1446
1639
});
1447
1640
}
1448
1641
}
···
1507
1700
1508
1701
private getUnionReference(union: Union): string | null {
1509
1702
return this.getReference(union, union.name, union.namespace);
1703
+
}
1704
+
1705
+
private getScalarReference(scalar: Scalar): string | null {
1706
+
// Built-in TypeSpec scalars (string, integer, boolean themselves) should not be referenced
1707
+
if (scalar.namespace?.name === "TypeSpec") return null;
1708
+
1709
+
// @inline scalars should be inlined, not referenced
1710
+
if (isInline(this.program, scalar)) return null;
1711
+
1712
+
// Scalars without names or namespace can't be referenced
1713
+
if (!scalar.name || !scalar.namespace) return null;
1714
+
1715
+
const defName = scalar.name.charAt(0).toLowerCase() + scalar.name.slice(1);
1716
+
const namespaceName = getNamespaceFullName(scalar.namespace);
1717
+
if (!namespaceName) return null;
1718
+
1719
+
// Local reference (same namespace) - use short ref
1720
+
if (
1721
+
this.currentLexiconId === namespaceName ||
1722
+
this.currentLexiconId === `${namespaceName}.defs`
1723
+
) {
1724
+
return `#${defName}`;
1725
+
}
1726
+
1727
+
// Cross-namespace reference
1728
+
return `${namespaceName}#${defName}`;
1510
1729
}
1511
1730
1512
1731
private modelToLexiconArray(
+2
packages/emitter/src/tsp-index.ts
+2
packages/emitter/src/tsp-index.ts
+2
packages/emitter/test/integration/atproto/input/app/bsky/actor/defs.tsp
+2
packages/emitter/test/integration/atproto/input/app/bsky/actor/defs.tsp
···
232
232
prioritizeFollowedUsers?: boolean;
233
233
}
234
234
235
+
@inline
235
236
@maxLength(640)
236
237
@maxGraphemes(64)
237
238
scalar InterestTag extends string;
···
292
293
@required did: did;
293
294
}
294
295
296
+
@inline
295
297
@maxLength(100)
296
298
scalar NudgeToken extends string;
297
299
+30
packages/emitter/test/spec/basic/input/com/example/scalarDefaults.tsp
+30
packages/emitter/test/spec/basic/input/com/example/scalarDefaults.tsp
···
1
+
import "@typelex/emitter";
2
+
3
+
namespace com.example.scalarDefaults {
4
+
/** Test default decorator on scalars */
5
+
model Main {
6
+
/** Uses string scalar with default */
7
+
mode?: Mode;
8
+
9
+
/** Uses integer scalar with default */
10
+
limit?: Limit;
11
+
12
+
/** Uses boolean scalar with default */
13
+
enabled?: Enabled;
14
+
}
15
+
16
+
/** A string type with a default value */
17
+
@default("standard")
18
+
@maxLength(50)
19
+
scalar Mode extends string;
20
+
21
+
/** An integer type with a default value */
22
+
@default(50)
23
+
@minValue(1)
24
+
@maxValue(100)
25
+
scalar Limit extends integer;
26
+
27
+
/** A boolean type with a default value */
28
+
@default(true)
29
+
scalar Enabled extends boolean;
30
+
}
+22
packages/emitter/test/spec/basic/input/com/example/scalarDefs.tsp
+22
packages/emitter/test/spec/basic/input/com/example/scalarDefs.tsp
···
1
+
import "@typelex/emitter";
2
+
3
+
namespace com.example.scalarDefs {
4
+
/** Scalar defs should create standalone defs like models and unions */
5
+
model Main {
6
+
/** Uses a custom string scalar with constraints */
7
+
tag?: Tag;
8
+
9
+
/** Uses a custom integer scalar with constraints */
10
+
count?: Count;
11
+
}
12
+
13
+
/** A custom string type with length constraints */
14
+
@maxLength(100)
15
+
@maxGraphemes(50)
16
+
scalar Tag extends string;
17
+
18
+
/** A custom integer type with value constraints */
19
+
@minValue(1)
20
+
@maxValue(100)
21
+
scalar Count extends integer;
22
+
}
+22
packages/emitter/test/spec/basic/input/com/example/scalarInline.tsp
+22
packages/emitter/test/spec/basic/input/com/example/scalarInline.tsp
···
1
+
import "@typelex/emitter";
2
+
3
+
namespace com.example.scalarInline {
4
+
/** Test inline decorator on scalars */
5
+
model Main {
6
+
/** Inline scalar - should not create a def */
7
+
tag?: Tag;
8
+
9
+
/** Non-inline scalar - should create a def */
10
+
category?: Category;
11
+
}
12
+
13
+
/** An inline scalar should be inlined at usage sites */
14
+
@inline
15
+
@maxLength(50)
16
+
@maxGraphemes(25)
17
+
scalar Tag extends string;
18
+
19
+
/** A regular scalar should create a standalone def */
20
+
@maxLength(100)
21
+
scalar Category extends string;
22
+
}
+53
packages/emitter/test/spec/basic/input/com/example/unionDefaults.tsp
+53
packages/emitter/test/spec/basic/input/com/example/unionDefaults.tsp
···
1
+
import "@typelex/emitter";
2
+
3
+
namespace com.example.unionDefaults {
4
+
/** Test default decorator on unions */
5
+
model Main {
6
+
/** Union with token refs and default */
7
+
eventMode?: EventMode;
8
+
9
+
/** Union with string literals and default */
10
+
sortOrder?: SortOrder;
11
+
12
+
/** Union with integer literals and default */
13
+
priority?: Priority;
14
+
}
15
+
16
+
/** Union of tokens with default pointing to a token */
17
+
@default(Inperson)
18
+
union EventMode {
19
+
Hybrid,
20
+
Inperson,
21
+
Virtual,
22
+
string,
23
+
}
24
+
25
+
/** A hybrid event */
26
+
@token
27
+
model Hybrid {}
28
+
29
+
/** An in-person event */
30
+
@token
31
+
model Inperson {}
32
+
33
+
/** A virtual event */
34
+
@token
35
+
model Virtual {}
36
+
37
+
/** Union of string literals with default */
38
+
@default("asc")
39
+
union SortOrder {
40
+
"asc",
41
+
"desc",
42
+
string,
43
+
}
44
+
45
+
/** Union of integer literals with default (closed enum) */
46
+
@default(1)
47
+
@closed
48
+
union Priority {
49
+
1,
50
+
2,
51
+
3,
52
+
}
53
+
}
+45
packages/emitter/test/spec/basic/output/com/example/scalarDefaults.json
+45
packages/emitter/test/spec/basic/output/com/example/scalarDefaults.json
···
1
+
{
2
+
"lexicon": 1,
3
+
"id": "com.example.scalarDefaults",
4
+
"defs": {
5
+
"main": {
6
+
"type": "object",
7
+
"properties": {
8
+
"mode": {
9
+
"type": "ref",
10
+
"ref": "#mode",
11
+
"description": "Uses string scalar with default"
12
+
},
13
+
"limit": {
14
+
"type": "ref",
15
+
"ref": "#limit",
16
+
"description": "Uses integer scalar with default"
17
+
},
18
+
"enabled": {
19
+
"type": "ref",
20
+
"ref": "#enabled",
21
+
"description": "Uses boolean scalar with default"
22
+
}
23
+
},
24
+
"description": "Test default decorator on scalars"
25
+
},
26
+
"mode": {
27
+
"type": "string",
28
+
"maxLength": 50,
29
+
"default": "standard",
30
+
"description": "A string type with a default value"
31
+
},
32
+
"limit": {
33
+
"type": "integer",
34
+
"minimum": 1,
35
+
"maximum": 100,
36
+
"default": 50,
37
+
"description": "An integer type with a default value"
38
+
},
39
+
"enabled": {
40
+
"type": "boolean",
41
+
"default": true,
42
+
"description": "A boolean type with a default value"
43
+
}
44
+
}
45
+
}
+34
packages/emitter/test/spec/basic/output/com/example/scalarDefs.json
+34
packages/emitter/test/spec/basic/output/com/example/scalarDefs.json
···
1
+
{
2
+
"lexicon": 1,
3
+
"id": "com.example.scalarDefs",
4
+
"defs": {
5
+
"main": {
6
+
"type": "object",
7
+
"properties": {
8
+
"tag": {
9
+
"type": "ref",
10
+
"ref": "#tag",
11
+
"description": "Uses a custom string scalar with constraints"
12
+
},
13
+
"count": {
14
+
"type": "ref",
15
+
"ref": "#count",
16
+
"description": "Uses a custom integer scalar with constraints"
17
+
}
18
+
},
19
+
"description": "Scalar defs should create standalone defs like models and unions"
20
+
},
21
+
"tag": {
22
+
"type": "string",
23
+
"maxLength": 100,
24
+
"maxGraphemes": 50,
25
+
"description": "A custom string type with length constraints"
26
+
},
27
+
"count": {
28
+
"type": "integer",
29
+
"minimum": 1,
30
+
"maximum": 100,
31
+
"description": "A custom integer type with value constraints"
32
+
}
33
+
}
34
+
}
+28
packages/emitter/test/spec/basic/output/com/example/scalarInline.json
+28
packages/emitter/test/spec/basic/output/com/example/scalarInline.json
···
1
+
{
2
+
"lexicon": 1,
3
+
"id": "com.example.scalarInline",
4
+
"defs": {
5
+
"main": {
6
+
"type": "object",
7
+
"properties": {
8
+
"tag": {
9
+
"type": "string",
10
+
"maxLength": 50,
11
+
"maxGraphemes": 25,
12
+
"description": "Inline scalar - should not create a def"
13
+
},
14
+
"category": {
15
+
"type": "ref",
16
+
"ref": "#category",
17
+
"description": "Non-inline scalar - should create a def"
18
+
}
19
+
},
20
+
"description": "Test inline decorator on scalars"
21
+
},
22
+
"category": {
23
+
"type": "string",
24
+
"maxLength": 100,
25
+
"description": "A regular scalar should create a standalone def"
26
+
}
27
+
}
28
+
}
+61
packages/emitter/test/spec/basic/output/com/example/unionDefaults.json
+61
packages/emitter/test/spec/basic/output/com/example/unionDefaults.json
···
1
+
{
2
+
"lexicon": 1,
3
+
"id": "com.example.unionDefaults",
4
+
"defs": {
5
+
"main": {
6
+
"type": "object",
7
+
"properties": {
8
+
"eventMode": {
9
+
"type": "ref",
10
+
"ref": "#eventMode",
11
+
"description": "Union with token refs and default"
12
+
},
13
+
"sortOrder": {
14
+
"type": "ref",
15
+
"ref": "#sortOrder",
16
+
"description": "Union with string literals and default"
17
+
},
18
+
"priority": {
19
+
"type": "ref",
20
+
"ref": "#priority",
21
+
"description": "Union with integer literals and default"
22
+
}
23
+
},
24
+
"description": "Test default decorator on unions"
25
+
},
26
+
"eventMode": {
27
+
"type": "string",
28
+
"knownValues": [
29
+
"com.example.unionDefaults#hybrid",
30
+
"com.example.unionDefaults#inperson",
31
+
"com.example.unionDefaults#virtual"
32
+
],
33
+
"default": "com.example.unionDefaults#inperson",
34
+
"description": "Union of tokens with default pointing to a token"
35
+
},
36
+
"hybrid": {
37
+
"type": "token",
38
+
"description": "A hybrid event"
39
+
},
40
+
"inperson": {
41
+
"type": "token",
42
+
"description": "An in-person event"
43
+
},
44
+
"virtual": {
45
+
"type": "token",
46
+
"description": "A virtual event"
47
+
},
48
+
"sortOrder": {
49
+
"type": "string",
50
+
"knownValues": ["asc", "desc"],
51
+
"default": "asc",
52
+
"description": "Union of string literals with default"
53
+
},
54
+
"priority": {
55
+
"type": "integer",
56
+
"enum": [1, 2, 3],
57
+
"default": 1,
58
+
"description": "Union of integer literals with default (closed enum)"
59
+
}
60
+
}
61
+
}