tjs-lang 0.2.8 → 0.3.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.
@@ -42,7 +42,7 @@ describe('Basic Types', () => {
42
42
  return x * 2
43
43
  }
44
44
  `)
45
- expect(getFirstFunc(metadata).params.x.type.kind).toBe('number')
45
+ expect(getFirstFunc(metadata).params.x.type.kind).toBe('integer')
46
46
  })
47
47
 
48
48
  test('boolean parameter', () => {
@@ -76,7 +76,7 @@ describe('Basic Types', () => {
76
76
  'string'
77
77
  )
78
78
  expect(getFirstFunc(metadata).params.user.type.shape?.age.kind).toBe(
79
- 'number'
79
+ 'integer'
80
80
  )
81
81
  })
82
82
 
@@ -101,7 +101,9 @@ describe('Basic Types', () => {
101
101
  }
102
102
  `)
103
103
  expect(getFirstFunc(metadata).params.nums.type.kind).toBe('array')
104
- expect(getFirstFunc(metadata).params.nums.type.items?.kind).toBe('number')
104
+ expect(getFirstFunc(metadata).params.nums.type.items?.kind).toBe(
105
+ 'integer'
106
+ )
105
107
  })
106
108
 
107
109
  test('array of objects', () => {
@@ -729,7 +731,7 @@ describe('Literal Types', () => {
729
731
  return n
730
732
  }
731
733
  `)
732
- expect(getFirstFunc(metadata).params.n.type.kind).toBe('number')
734
+ expect(getFirstFunc(metadata).params.n.type.kind).toBe('integer')
733
735
  })
734
736
 
735
737
  test('literal union type alias emits TJS Union', () => {
@@ -137,6 +137,70 @@ describe('Type()', () => {
137
137
  expect(Email.check(Email.example)).toBe(true)
138
138
  })
139
139
  })
140
+
141
+ describe('Schema examples support', () => {
142
+ it('extracts examples from schema metadata', () => {
143
+ const Username = Type(
144
+ 'username',
145
+ s.string.meta({ examples: ['alice', 'bob', 'charlie'] })
146
+ )
147
+
148
+ expect(Username.examples).toEqual(['alice', 'bob', 'charlie'])
149
+ })
150
+
151
+ it('sets example to first schema example when no explicit example given', () => {
152
+ const Username = Type(
153
+ 'username',
154
+ s.string.meta({ examples: ['alice', 'bob'] })
155
+ )
156
+
157
+ expect(Username.example).toBe('alice')
158
+ })
159
+
160
+ it('preserves explicit example over schema examples', () => {
161
+ const Username = Type(
162
+ 'username',
163
+ s.string.meta({ examples: ['alice', 'bob'] }),
164
+ 'explicit_user'
165
+ )
166
+
167
+ expect(Username.example).toBe('explicit_user')
168
+ expect(Username.examples).toEqual(['alice', 'bob'])
169
+ })
170
+
171
+ it('has no examples when schema lacks metadata', () => {
172
+ const Name = Type('name', s.string)
173
+
174
+ expect(Name.examples).toBeUndefined()
175
+ })
176
+
177
+ it('has no examples for predicate-based types', () => {
178
+ const Even = Type(
179
+ 'even number',
180
+ (n) => typeof n === 'number' && n % 2 === 0
181
+ )
182
+
183
+ expect(Even.examples).toBeUndefined()
184
+ })
185
+
186
+ it('has no examples for simple form', () => {
187
+ const Name = Type('name', 'Alice')
188
+
189
+ expect(Name.examples).toBeUndefined()
190
+ expect(Name.example).toBe('Alice')
191
+ })
192
+
193
+ it('validates using schema even with examples', () => {
194
+ const ShortString = Type(
195
+ 'short string',
196
+ s.string.max(5).meta({ examples: ['hi', 'hey'] })
197
+ )
198
+
199
+ expect(ShortString.check('hi')).toBe(true)
200
+ expect(ShortString.check('toolong')).toBe(false)
201
+ expect(ShortString.examples).toEqual(['hi', 'hey'])
202
+ })
203
+ })
140
204
  })
141
205
 
142
206
  describe('Built-in Types', () => {
package/src/types/Type.ts CHANGED
@@ -40,8 +40,10 @@ export interface RuntimeType<T = unknown> {
40
40
  readonly schema?: Schema
41
41
  /** The predicate function (if predicate-based) */
42
42
  readonly predicate?: (value: unknown) => boolean
43
- /** Example value (for documentation and implicit testing) */
43
+ /** Example value (for documentation and signature testing) */
44
44
  readonly example?: T
45
+ /** Multiple example values (from schema metadata, for autocomplete hints) */
46
+ readonly examples?: T[]
45
47
  /** Default value (for instantiation) */
46
48
  readonly default?: T
47
49
  /** Brand for type identification */
@@ -150,6 +152,24 @@ export function Type<T = unknown>(
150
152
  description = schemaToDescription(schema)
151
153
  }
152
154
 
155
+ // Extract examples from schema metadata (if any)
156
+ let examples: T[] | undefined
157
+ if (schema) {
158
+ const jsonSchema = (schema as any)?.schema ?? schema
159
+ if (
160
+ jsonSchema &&
161
+ typeof jsonSchema === 'object' &&
162
+ Array.isArray((jsonSchema as any).examples)
163
+ ) {
164
+ examples = (jsonSchema as any).examples as T[]
165
+ }
166
+ }
167
+
168
+ // If no explicit example was provided, use first schema example for autocomplete
169
+ if (example === undefined && examples && examples.length > 0) {
170
+ example = examples[0]
171
+ }
172
+
153
173
  // Build the check function
154
174
  const check = (value: unknown): value is T => {
155
175
  if (predicate) {
@@ -167,6 +187,7 @@ export function Type<T = unknown>(
167
187
  schema,
168
188
  predicate,
169
189
  example,
190
+ examples,
170
191
  default: defaultValue,
171
192
  __runtimeType: true as const,
172
193
  }
@@ -134,7 +134,7 @@ describe('Transpiler Integration', () => {
134
134
  expect(signature.parameters.query.type.kind).toBe('string')
135
135
  expect(signature.parameters.query.required).toBe(true)
136
136
  expect(signature.parameters.query.description).toBe('Search terms')
137
- expect(signature.parameters.limit.type.kind).toBe('number')
137
+ expect(signature.parameters.limit.type.kind).toBe('integer')
138
138
  expect(signature.parameters.limit.required).toBe(false)
139
139
  expect(signature.parameters.limit.default).toBe(10)
140
140
  expect(signature.parameters.limit.description).toBe('Max results')
@@ -153,7 +153,7 @@ describe('Transpiler Integration', () => {
153
153
  expect(signature.parameters.user.type.kind).toBe('object')
154
154
  expect(signature.parameters.user.type.shape?.name.kind).toBe('string')
155
155
  expect(signature.parameters.user.type.shape?.email.kind).toBe('string')
156
- expect(signature.parameters.user.type.shape?.age.kind).toBe('number')
156
+ expect(signature.parameters.user.type.shape?.age.kind).toBe('integer')
157
157
  expect(signature.parameters.user.required).toBe(true)
158
158
 
159
159
  expect(signature.parameters.options.type.kind).toBe('object')
@@ -383,16 +383,16 @@ describe('Transpiler Integration', () => {
383
383
  let answer = ''
384
384
  let valid = false
385
385
  let tries = 0
386
-
386
+
387
387
  while (!valid && tries < 3) {
388
388
  answer = llmPredict({ prompt: question })
389
389
  tries = tries + 1
390
-
390
+
391
391
  if (answer == 'A' || answer == 'B' || answer == 'C' || answer == 'D') {
392
392
  valid = true
393
393
  }
394
394
  }
395
-
395
+
396
396
  return { answer, tries, valid }
397
397
  }
398
398
  `)
@@ -772,9 +772,9 @@ describe('Transpiler Integration', () => {
772
772
  const ast = ajs(`
773
773
  function test() {
774
774
  let d = Date('2024-06-15T10:30:00Z')
775
- return {
776
- year: d.year,
777
- month: d.month,
775
+ return {
776
+ year: d.year,
777
+ month: d.month,
778
778
  day: d.day,
779
779
  hours: d.hours,
780
780
  minutes: d.minutes
@@ -826,7 +826,7 @@ describe('Transpiler Integration', () => {
826
826
  const ast = ajs(`
827
827
  function test() {
828
828
  let d = Date('2024-06-15T14:30:45Z')
829
- return {
829
+ return {
830
830
  iso: d.format('ISO'),
831
831
  date: d.format('date'),
832
832
  custom: d.format('YYYY-MM-DD')
@@ -847,7 +847,7 @@ describe('Transpiler Integration', () => {
847
847
  function test() {
848
848
  let a = Date('2024-01-15')
849
849
  let b = Date('2024-01-20')
850
- return {
850
+ return {
851
851
  aBeforeB: a.isBefore(b),
852
852
  aAfterB: a.isAfter(b)
853
853
  }
@@ -145,6 +145,7 @@ export const llmPredictBattery = defineAtom(
145
145
  responseFormat: s.any.optional,
146
146
  }),
147
147
  s.object({
148
+ role: s.string.optional,
148
149
  content: s.string.optional,
149
150
  tool_calls: s.array(s.any).optional,
150
151
  }),
@@ -189,6 +190,7 @@ export const llmVision = defineAtom(
189
190
  responseFormat: s.any.optional,
190
191
  }),
191
192
  s.object({
193
+ role: s.string.optional,
192
194
  content: s.string.optional,
193
195
  tool_calls: s.array(s.any).optional,
194
196
  }),