@api-client/core 0.17.7 → 0.18.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.
Files changed (56) hide show
  1. package/build/src/amf/ApiSchemaGenerator.d.ts +2 -2
  2. package/build/src/amf/ApiSchemaGenerator.d.ts.map +1 -1
  3. package/build/src/amf/ApiSchemaGenerator.js.map +1 -1
  4. package/build/src/amf/ApiSchemaValues.d.ts.map +1 -1
  5. package/build/src/amf/ApiSchemaValues.js +8 -1
  6. package/build/src/amf/ApiSchemaValues.js.map +1 -1
  7. package/build/src/amf/shape/ShapeBase.d.ts +1 -1
  8. package/build/src/amf/shape/ShapeBase.d.ts.map +1 -1
  9. package/build/src/amf/shape/ShapeBase.js.map +1 -1
  10. package/build/src/amf/shape/ShapeJsonSchemaGenerator.d.ts +1 -1
  11. package/build/src/amf/shape/ShapeJsonSchemaGenerator.d.ts.map +1 -1
  12. package/build/src/amf/shape/ShapeJsonSchemaGenerator.js +7 -1
  13. package/build/src/amf/shape/ShapeJsonSchemaGenerator.js.map +1 -1
  14. package/build/src/amf/shape/ShapeXmlSchemaGenerator.d.ts +1 -1
  15. package/build/src/amf/shape/ShapeXmlSchemaGenerator.d.ts.map +1 -1
  16. package/build/src/amf/shape/ShapeXmlSchemaGenerator.js +8 -2
  17. package/build/src/amf/shape/ShapeXmlSchemaGenerator.js.map +1 -1
  18. package/build/src/mocking/RandExp.d.ts +55 -0
  19. package/build/src/mocking/RandExp.d.ts.map +1 -0
  20. package/build/src/mocking/RandExp.js +302 -0
  21. package/build/src/mocking/RandExp.js.map +1 -0
  22. package/build/src/mocking/lib/ret.d.ts +16 -0
  23. package/build/src/mocking/lib/ret.d.ts.map +1 -0
  24. package/build/src/mocking/lib/ret.js +284 -0
  25. package/build/src/mocking/lib/ret.js.map +1 -0
  26. package/build/src/modeling/Bindings.d.ts +0 -4
  27. package/build/src/modeling/Bindings.d.ts.map +1 -1
  28. package/build/src/modeling/Bindings.js.map +1 -1
  29. package/build/src/modeling/DomainEntity.js +3 -3
  30. package/build/src/modeling/DomainEntity.js.map +1 -1
  31. package/build/src/modeling/DomainProperty.d.ts +18 -0
  32. package/build/src/modeling/DomainProperty.d.ts.map +1 -1
  33. package/build/src/modeling/DomainProperty.js +31 -0
  34. package/build/src/modeling/DomainProperty.js.map +1 -1
  35. package/build/src/modeling/amf/ShapeGenerator.js +3 -3
  36. package/build/src/modeling/amf/ShapeGenerator.js.map +1 -1
  37. package/build/src/modeling/types.d.ts +4 -0
  38. package/build/src/modeling/types.d.ts.map +1 -1
  39. package/build/src/modeling/types.js.map +1 -1
  40. package/build/tsconfig.tsbuildinfo +1 -1
  41. package/data/models/example-generator-api.json +9 -9
  42. package/package.json +1 -1
  43. package/src/amf/ApiSchemaGenerator.ts +2 -2
  44. package/src/amf/ApiSchemaValues.ts +8 -1
  45. package/src/amf/shape/ShapeBase.ts +1 -1
  46. package/src/amf/shape/ShapeJsonSchemaGenerator.ts +7 -2
  47. package/src/amf/shape/ShapeXmlSchemaGenerator.ts +8 -3
  48. package/src/mocking/RandExp.ts +335 -0
  49. package/src/mocking/lib/ret.ts +279 -0
  50. package/src/modeling/Bindings.ts +0 -4
  51. package/src/modeling/DomainEntity.ts +3 -3
  52. package/src/modeling/DomainProperty.ts +33 -0
  53. package/src/modeling/amf/ShapeGenerator.ts +3 -3
  54. package/src/modeling/types.ts +4 -0
  55. package/tests/unit/modeling/amf/shape_generator.spec.ts +3 -8
  56. package/tests/unit/modeling/domain_property.spec.ts +335 -0
@@ -0,0 +1,279 @@
1
+ // Regular Expression Tokenizer
2
+ // https://github.com/fent/ret.js
3
+
4
+ export enum types {
5
+ ROOT = 0,
6
+ GROUP = 1,
7
+ POSITION = 2,
8
+ SET = 3,
9
+ RANGE = 4,
10
+ REPETITION = 5,
11
+ REFERENCE = 6,
12
+ CHAR = 7,
13
+ }
14
+
15
+ export interface Token {
16
+ type: types
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ [key: string]: any
19
+ }
20
+ const wordBoundary = (): Token => ({ type: types.POSITION, value: 'b' })
21
+ const nonWordBoundary = (): Token => ({ type: types.POSITION, value: 'B' })
22
+ const begin = (): Token => ({ type: types.POSITION, value: '^' })
23
+ const end = (): Token => ({ type: types.POSITION, value: '$' })
24
+
25
+ const ints = (): Token[] => [{ type: types.RANGE, from: 48, to: 57 }]
26
+ const words = (): Token[] => [
27
+ { type: types.CHAR, value: 95 },
28
+ { type: types.RANGE, from: 97, to: 122 },
29
+ { type: types.RANGE, from: 65, to: 90 },
30
+ ...ints(),
31
+ ]
32
+ const whitespace = (): Token[] => [
33
+ { type: types.CHAR, value: 9 },
34
+ { type: types.CHAR, value: 10 },
35
+ { type: types.CHAR, value: 11 },
36
+ { type: types.CHAR, value: 12 },
37
+ { type: types.CHAR, value: 13 },
38
+ { type: types.CHAR, value: 32 },
39
+ { type: types.CHAR, value: 160 },
40
+ { type: types.CHAR, value: 5760 },
41
+ { type: types.CHAR, value: 6158 },
42
+ { type: types.CHAR, value: 8192 },
43
+ { type: types.CHAR, value: 8193 },
44
+ { type: types.CHAR, value: 8194 },
45
+ { type: types.CHAR, value: 8195 },
46
+ { type: types.CHAR, value: 8196 },
47
+ { type: types.CHAR, value: 8197 },
48
+ { type: types.CHAR, value: 8198 },
49
+ { type: types.CHAR, value: 8199 },
50
+ { type: types.CHAR, value: 8200 },
51
+ { type: types.CHAR, value: 8201 },
52
+ { type: types.CHAR, value: 8202 },
53
+ { type: types.CHAR, value: 8232 },
54
+ { type: types.CHAR, value: 8233 },
55
+ { type: types.CHAR, value: 8239 },
56
+ { type: types.CHAR, value: 8287 },
57
+ { type: types.CHAR, value: 12288 },
58
+ { type: types.CHAR, value: 65279 },
59
+ ]
60
+ const notanychar = (): Token[] => [
61
+ { type: types.CHAR, value: 10 },
62
+ { type: types.CHAR, value: 13 },
63
+ { type: types.CHAR, value: 8232 },
64
+ { type: types.CHAR, value: 8233 },
65
+ ]
66
+
67
+ const words_ = (): Token => ({ type: types.SET, set: words(), not: false })
68
+ const notWords = (): Token => ({ type: types.SET, set: words(), not: true })
69
+ const ints_ = (): Token => ({ type: types.SET, set: ints(), not: false })
70
+ const notInts = (): Token => ({ type: types.SET, set: ints(), not: true })
71
+ const whitespace_ = (): Token => ({ type: types.SET, set: whitespace(), not: false })
72
+ const notWhitespace = (): Token => ({ type: types.SET, set: whitespace(), not: true })
73
+ const anyChar = (): Token => ({ type: types.SET, set: notanychar(), not: true })
74
+
75
+ const specialChars: Record<string, number> = {
76
+ '0': 0,
77
+ 't': 9,
78
+ 'n': 10,
79
+ 'v': 11,
80
+ 'f': 12,
81
+ 'r': 13,
82
+ }
83
+
84
+ const strToChars = (str: string): string => {
85
+ const chars = /(\[\\b\])|(\\)?\\(?:u([A-F0-9]{4})|x([A-F0-9]{2})|(0?[0-7]{2})|c([@A-Z[\\\]^?])|([0tnvfr]))/g
86
+ return str.replace(chars, (match, b, s, uh, xh, o, c, sp) => {
87
+ if (s) return match
88
+ const code = b
89
+ ? 8
90
+ : uh
91
+ ? parseInt(uh, 16)
92
+ : xh
93
+ ? parseInt(xh, 16)
94
+ : o
95
+ ? parseInt(o, 8)
96
+ : c
97
+ ? '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ ?'.indexOf(c)
98
+ : specialChars[sp]
99
+ let ch = String.fromCharCode(code)
100
+ if (/[[{}^$.|?*+()]/.test(ch)) {
101
+ ch = `\\${ch}`
102
+ }
103
+ return ch
104
+ })
105
+ }
106
+
107
+ const tokenizeClass = (str: string, regexpStr: string): [Token[], number] => {
108
+ const tokens: Token[] = []
109
+ const classTokens = /\\(?:(w)|(d)|(s)|(W)|(D)|(S))|((?:(?:\\)(.)|([^\]\\]))-(?:\\)?([^\]]))|(\])|(?:\\)?(.)/g
110
+ let match
111
+ let char
112
+ while ((match = classTokens.exec(str)) !== null) {
113
+ if (match[1]) {
114
+ tokens.push(words_())
115
+ } else if (match[2]) {
116
+ tokens.push(ints_())
117
+ } else if (match[3]) {
118
+ tokens.push(whitespace_())
119
+ } else if (match[4]) {
120
+ tokens.push(notWords())
121
+ } else if (match[5]) {
122
+ tokens.push(notInts())
123
+ } else if (match[6]) {
124
+ tokens.push(notWhitespace())
125
+ } else if (match[7]) {
126
+ tokens.push({
127
+ type: types.RANGE,
128
+ from: (match[8] || match[9]).charCodeAt(0),
129
+ to: match[10].charCodeAt(0),
130
+ })
131
+ } else if ((char = match[12])) {
132
+ tokens.push({ type: types.CHAR, value: char.charCodeAt(0) })
133
+ } else {
134
+ return [tokens, classTokens.lastIndex]
135
+ }
136
+ }
137
+ throw new SyntaxError(`Invalid regular expression: /${regexpStr}/: Unterminated character class`)
138
+ }
139
+
140
+ const error = (regexpStr: string, msg: string): never => {
141
+ throw new SyntaxError(`Invalid regular expression: /${regexpStr}/: ${msg}`)
142
+ }
143
+
144
+ export const ret = (regexpStr: string): Token => {
145
+ let i = 0
146
+ const root: Token = { type: types.ROOT, stack: [] }
147
+ let p: Token = root
148
+ let stack: Token[] = root.stack
149
+ const groupStack: Token[] = []
150
+
151
+ const repeatErr = (i: number) => error(regexpStr, `Nothing to repeat at column ${i - 1}`)
152
+ const chars = strToChars(regexpStr)
153
+
154
+ while (i < chars.length) {
155
+ let c = chars[i++]
156
+ switch (c) {
157
+ case '\\':
158
+ c = chars[i++]
159
+ switch (c) {
160
+ case 'b':
161
+ stack.push(wordBoundary())
162
+ break
163
+ case 'B':
164
+ stack.push(nonWordBoundary())
165
+ break
166
+ case 'w':
167
+ stack.push(words_())
168
+ break
169
+ case 'W':
170
+ stack.push(notWords())
171
+ break
172
+ case 'd':
173
+ stack.push(ints_())
174
+ break
175
+ case 'D':
176
+ stack.push(notInts())
177
+ break
178
+ case 's':
179
+ stack.push(whitespace_())
180
+ break
181
+ case 'S':
182
+ stack.push(notWhitespace())
183
+ break
184
+ default:
185
+ if (/\d/.test(c)) {
186
+ stack.push({ type: types.REFERENCE, value: parseInt(c, 10) })
187
+ } else {
188
+ stack.push({ type: types.CHAR, value: c.charCodeAt(0) })
189
+ }
190
+ }
191
+ break
192
+ case '^':
193
+ stack.push(begin())
194
+ break
195
+ case '$':
196
+ stack.push(end())
197
+ break
198
+ case '[': {
199
+ let not = false
200
+ if (chars[i] === '^') {
201
+ not = true
202
+ i++
203
+ }
204
+ const [set, lastIndex] = tokenizeClass(chars.slice(i), regexpStr)
205
+ i += lastIndex
206
+ stack.push({ type: types.SET, set, not })
207
+ break
208
+ }
209
+ case '.':
210
+ stack.push(anyChar())
211
+ break
212
+ case '(': {
213
+ const group: Token = { type: types.GROUP, stack: [], remember: true }
214
+ if (chars[i] === '?') {
215
+ c = chars[i + 1]
216
+ i += 2
217
+ if (c === '=') {
218
+ group.followedBy = true
219
+ } else if (c === '!') {
220
+ group.notFollowedBy = true
221
+ } else if (c !== ':') {
222
+ error(regexpStr, `Invalid group, character '${c}' after '?' at column ${i - 1}`)
223
+ }
224
+ group.remember = false
225
+ }
226
+ stack.push(group)
227
+ groupStack.push(p)
228
+ p = group
229
+ stack = group.stack
230
+ break
231
+ }
232
+ case ')':
233
+ if (groupStack.length === 0) error(regexpStr, `Unmatched ) at column ${i - 1}`)
234
+ p = groupStack.pop() as Token
235
+ stack = p.options ? p.options[p.options.length - 1] : p.stack
236
+ break
237
+ case '|':
238
+ if (!p.options) {
239
+ p.options = [p.stack]
240
+ delete p.stack
241
+ }
242
+ {
243
+ const newStack: Token[] = []
244
+ p.options.push(newStack)
245
+ stack = newStack
246
+ }
247
+ break
248
+ case '{': {
249
+ const repetition = /^(\d+)(,(\d+)?)?\}/.exec(chars.slice(i))
250
+ if (repetition) {
251
+ if (stack.length === 0) repeatErr(i)
252
+ const min = parseInt(repetition[1], 10)
253
+ const max = repetition[2] ? (repetition[3] ? parseInt(repetition[3], 10) : Infinity) : min
254
+ i += repetition[0].length
255
+ stack.push({ type: types.REPETITION, min, max, value: stack.pop() })
256
+ } else {
257
+ stack.push({ type: types.CHAR, value: 123 })
258
+ }
259
+ break
260
+ }
261
+ case '?':
262
+ if (stack.length === 0) repeatErr(i)
263
+ stack.push({ type: types.REPETITION, min: 0, max: 1, value: stack.pop() })
264
+ break
265
+ case '+':
266
+ if (stack.length === 0) repeatErr(i)
267
+ stack.push({ type: types.REPETITION, min: 1, max: Infinity, value: stack.pop() })
268
+ break
269
+ case '*':
270
+ if (stack.length === 0) repeatErr(i)
271
+ stack.push({ type: types.REPETITION, min: 0, max: Infinity, value: stack.pop() })
272
+ break
273
+ default:
274
+ stack.push({ type: types.CHAR, value: c.charCodeAt(0) })
275
+ }
276
+ }
277
+ if (groupStack.length !== 0) error(regexpStr, 'Unterminated group')
278
+ return root
279
+ }
@@ -75,10 +75,6 @@ export interface PropertyWebBindings {
75
75
  * The list of file mime types.
76
76
  */
77
77
  fileTypes?: string[]
78
- /**
79
- * The patter to use wit a string scalar
80
- */
81
- pattern?: string
82
78
  /**
83
79
  * Whether the attribute is hidden in the schema (not a part of it).
84
80
  */
@@ -786,9 +786,9 @@ export class DomainEntity extends DomainElement {
786
786
  toExample(mime: string, opts: IShapeRenderOptions = {}): string | number | boolean | null | undefined {
787
787
  const shape = this.toApiShape()
788
788
  const generator = new ApiSchemaGenerator(mime, {
789
- renderExamples: typeof opts.renderExamples === 'boolean' ? opts.renderExamples : true,
790
- renderMocked: typeof opts.renderMocked === 'boolean' ? opts.renderMocked : true,
791
- renderOptional: typeof opts.renderOptional === 'boolean' ? opts.renderOptional : true,
789
+ renderExamples: opts.renderExamples ?? true,
790
+ renderMocked: opts.renderMocked ?? true,
791
+ renderOptional: opts.renderOptional ?? true,
792
792
  selectedUnions: opts.selectedUnions,
793
793
  })
794
794
  return generator.generate(shape)
@@ -23,6 +23,8 @@ import type { DomainEntity } from './DomainEntity.js'
23
23
  import type { IApiPropertyShape } from '../amf/definitions/Shapes.js'
24
24
  import type { PropertySchema } from './types.js'
25
25
  import { DataSemantics, isPropertySemantic, type SemanticType, type AppliedDataSemantic } from './Semantics.js'
26
+ import { ApiSchemaGenerator } from '../amf/ApiSchemaGenerator.js'
27
+ import type { IShapeRenderOptions } from '../amf/shape/ShapeBase.js'
26
28
 
27
29
  export interface DomainPropertySchema extends DomainElementSchema {
28
30
  kind: typeof DomainPropertyKind
@@ -545,6 +547,37 @@ export class DomainProperty extends DomainElement {
545
547
  return serializer.property(this)
546
548
  }
547
549
 
550
+ /**
551
+ * Generates an example value for this property based on its schema and type.
552
+ *
553
+ * This method converts the property to an AMF shape and then generates
554
+ * an example value using the ApiSchemaGenerator. The generated example
555
+ * respects the property's type, constraints, and any defined schema.
556
+ *
557
+ * @param mime The mime type of the example (e.g., 'application/json', 'application/xml').
558
+ * @param opts Optional configuration for example generation.
559
+ * @returns The generated example value.
560
+ * @example
561
+ * ```typescript
562
+ * const example = property.toExample('application/json');
563
+ * console.log(example);
564
+ * ```
565
+ */
566
+ toExample(mime: string, opts: IShapeRenderOptions = {}): string | number | boolean | null | undefined {
567
+ // TODO: add support for semantic extensions and generate example values based on them.
568
+ const shape = this.toApiShape()
569
+ if (!shape.range) {
570
+ return undefined
571
+ }
572
+ const generator = new ApiSchemaGenerator(mime, {
573
+ renderExamples: opts.renderExamples ?? true,
574
+ renderMocked: opts.renderMocked ?? true,
575
+ renderOptional: opts.renderOptional ?? true,
576
+ selectedUnions: opts.selectedUnions,
577
+ })
578
+ return generator.generate(shape)
579
+ }
580
+
548
581
  /**
549
582
  * Adds or updates a semantic to the property.
550
583
  * @param semantic The semantic to add to the property.
@@ -464,11 +464,11 @@ export class ShapeGenerator {
464
464
  if (bindings?.xml) {
465
465
  result.xmlSerialization = bindings.xml
466
466
  }
467
- if (bindings?.pattern) {
468
- result.pattern = bindings.pattern
469
- }
470
467
  const { schema, type } = input
471
468
  if (schema) {
469
+ if (schema.pattern) {
470
+ result.pattern = schema.pattern
471
+ }
472
472
  if (typeof schema.multipleOf === 'number') {
473
473
  result.multipleOf = schema.multipleOf
474
474
  }
@@ -183,6 +183,10 @@ export interface PropertySchema {
183
183
  * They are always encoded as strings. The actual type is defined in the `dataType` property.
184
184
  */
185
185
  examples?: string[]
186
+ /**
187
+ * The pattern to use with a string scalar.
188
+ */
189
+ pattern?: string
186
190
  }
187
191
 
188
192
  /**
@@ -452,14 +452,9 @@ test.group('property() / with web bindings', (group) => {
452
452
 
453
453
  test('sets the pattern', ({ assert }) => {
454
454
  const p1 = e1.addProperty({ key: 'p1', type: 'string', info: { name: 'p1' } })
455
- p1.bindings = [
456
- {
457
- type: 'web',
458
- schema: {
459
- pattern: 'a-z',
460
- },
461
- },
462
- ]
455
+ p1.schema = {
456
+ pattern: 'a-z',
457
+ }
463
458
  const result = p1.toApiShape()
464
459
  const string = result.range as IApiScalarShape
465
460
  assert.equal(string.pattern, 'a-z')