@pyreon/feature 0.10.0 → 0.11.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/src/index.ts CHANGED
@@ -1,11 +1,11 @@
1
- export { defineFeature } from './define-feature'
2
- export type { FieldInfo, FieldType, ReferenceSchema } from './schema'
1
+ export { defineFeature } from "./define-feature"
2
+ export type { FieldInfo, FieldType, ReferenceSchema } from "./schema"
3
3
  export {
4
4
  defaultInitialValues,
5
5
  extractFields,
6
6
  isReference,
7
7
  reference,
8
- } from './schema'
8
+ } from "./schema"
9
9
  export type {
10
10
  Feature,
11
11
  FeatureConfig,
@@ -13,5 +13,6 @@ export type {
13
13
  FeatureStore,
14
14
  FeatureTableOptions,
15
15
  FeatureTableResult,
16
+ InferSchemaValues,
16
17
  ListOptions,
17
- } from './types'
18
+ } from "./types"
package/src/schema.ts CHANGED
@@ -21,18 +21,18 @@ export interface FieldInfo {
21
21
  }
22
22
 
23
23
  export type FieldType =
24
- | 'string'
25
- | 'number'
26
- | 'boolean'
27
- | 'date'
28
- | 'enum'
29
- | 'array'
30
- | 'object'
31
- | 'reference'
32
- | 'unknown'
24
+ | "string"
25
+ | "number"
26
+ | "boolean"
27
+ | "date"
28
+ | "enum"
29
+ | "array"
30
+ | "object"
31
+ | "reference"
32
+ | "unknown"
33
33
 
34
34
  /** Symbol used to tag reference schema objects. */
35
- const REFERENCE_TAG = Symbol.for('pyreon:feature:reference')
35
+ const REFERENCE_TAG = Symbol.for("pyreon:feature:reference")
36
36
 
37
37
  /**
38
38
  * Metadata carried by a reference schema.
@@ -61,7 +61,7 @@ export interface ReferenceSchema {
61
61
  export function isReference(value: unknown): value is ReferenceSchema {
62
62
  return (
63
63
  value !== null &&
64
- typeof value === 'object' &&
64
+ typeof value === "object" &&
65
65
  (value as Record<symbol, unknown>)[REFERENCE_TAG] === true
66
66
  )
67
67
  }
@@ -93,7 +93,7 @@ export function reference(feature: { name: string }): ReferenceSchema {
93
93
  success: boolean
94
94
  error?: { issues: { message: string }[] }
95
95
  } {
96
- if (typeof value === 'string' || typeof value === 'number') {
96
+ if (typeof value === "string" || typeof value === "number") {
97
97
  return { success: true }
98
98
  }
99
99
  return {
@@ -113,7 +113,7 @@ export function reference(feature: { name: string }): ReferenceSchema {
113
113
  _featureName: featureName,
114
114
  safeParse: validateRef,
115
115
  safeParseAsync: async (value: unknown) => validateRef(value),
116
- _def: { typeName: 'ZodString' },
116
+ _def: { typeName: "ZodString" },
117
117
  }
118
118
  }
119
119
 
@@ -123,8 +123,8 @@ export function reference(feature: { name: string }): ReferenceSchema {
123
123
  */
124
124
  function nameToLabel(name: string): string {
125
125
  return name
126
- .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase → camel Case
127
- .replace(/[_-]/g, ' ') // snake_case/kebab-case → spaces
126
+ .replace(/([a-z])([A-Z])/g, "$1 $2") // camelCase → camel Case
127
+ .replace(/[_-]/g, " ") // snake_case/kebab-case → spaces
128
128
  .replace(/\b\w/g, (c) => c.toUpperCase()) // capitalize words
129
129
  }
130
130
 
@@ -141,14 +141,14 @@ function detectFieldType(zodField: unknown): {
141
141
  // Check for reference fields first
142
142
  if (isReference(zodField)) {
143
143
  return {
144
- type: 'reference',
144
+ type: "reference",
145
145
  optional: false,
146
146
  referenceTo: zodField._featureName,
147
147
  }
148
148
  }
149
149
 
150
- if (!zodField || typeof zodField !== 'object') {
151
- return { type: 'unknown', optional: false }
150
+ if (!zodField || typeof zodField !== "object") {
151
+ return { type: "unknown", optional: false }
152
152
  }
153
153
 
154
154
  const field = zodField as Record<string, unknown>
@@ -161,13 +161,13 @@ function detectFieldType(zodField: unknown): {
161
161
  const getTypeName = (obj: Record<string, unknown>): string | undefined => {
162
162
  // v3 path
163
163
  const def = obj._def as Record<string, unknown> | undefined
164
- if (def?.typeName && typeof def.typeName === 'string') {
164
+ if (def?.typeName && typeof def.typeName === "string") {
165
165
  return def.typeName
166
166
  }
167
167
  // v4 path
168
168
  const zod = obj._zod as Record<string, unknown> | undefined
169
169
  const zodDef = zod?.def as Record<string, unknown> | undefined
170
- if (zodDef?.type && typeof zodDef.type === 'string') {
170
+ if (zodDef?.type && typeof zodDef.type === "string") {
171
171
  return zodDef.type
172
172
  }
173
173
  return undefined
@@ -177,16 +177,15 @@ function detectFieldType(zodField: unknown): {
177
177
 
178
178
  // Unwrap optional/nullable
179
179
  if (
180
- typeName === 'ZodOptional' ||
181
- typeName === 'ZodNullable' ||
182
- typeName === 'optional' ||
183
- typeName === 'nullable'
180
+ typeName === "ZodOptional" ||
181
+ typeName === "ZodNullable" ||
182
+ typeName === "optional" ||
183
+ typeName === "nullable"
184
184
  ) {
185
185
  optional = true
186
186
  const def = inner._def as Record<string, unknown> | undefined
187
- const innerType =
188
- def?.innerType ?? (inner._zod as Record<string, unknown>)?.def
189
- if (innerType && typeof innerType === 'object') {
187
+ const innerType = def?.innerType ?? (inner._zod as Record<string, unknown>)?.def
188
+ if (innerType && typeof innerType === "object") {
190
189
  inner = innerType as Record<string, unknown>
191
190
  }
192
191
  }
@@ -194,32 +193,32 @@ function detectFieldType(zodField: unknown): {
194
193
  const innerTypeName = getTypeName(inner) ?? typeName
195
194
 
196
195
  // Map Zod type names to our FieldType
197
- if (!innerTypeName) return { type: 'unknown', optional }
196
+ if (!innerTypeName) return { type: "unknown", optional }
198
197
 
199
198
  const typeMap: Record<string, FieldType> = {
200
- ZodString: 'string',
201
- ZodNumber: 'number',
202
- ZodBoolean: 'boolean',
203
- ZodDate: 'date',
204
- ZodEnum: 'enum',
205
- ZodNativeEnum: 'enum',
206
- ZodArray: 'array',
207
- ZodObject: 'object',
199
+ ZodString: "string",
200
+ ZodNumber: "number",
201
+ ZodBoolean: "boolean",
202
+ ZodDate: "date",
203
+ ZodEnum: "enum",
204
+ ZodNativeEnum: "enum",
205
+ ZodArray: "array",
206
+ ZodObject: "object",
208
207
  // v4 names
209
- string: 'string',
210
- number: 'number',
211
- boolean: 'boolean',
212
- date: 'date',
213
- enum: 'enum',
214
- array: 'array',
215
- object: 'object',
208
+ string: "string",
209
+ number: "number",
210
+ boolean: "boolean",
211
+ date: "date",
212
+ enum: "enum",
213
+ array: "array",
214
+ object: "object",
216
215
  }
217
216
 
218
- const type = typeMap[innerTypeName] ?? 'string'
217
+ const type = typeMap[innerTypeName] ?? "string"
219
218
 
220
219
  // Extract enum values
221
220
  let enumValues: (string | number)[] | undefined
222
- if (type === 'enum') {
221
+ if (type === "enum") {
223
222
  const def = inner._def as Record<string, unknown> | undefined
224
223
  if (def?.values && Array.isArray(def.values)) {
225
224
  enumValues = def.values as (string | number)[]
@@ -255,7 +254,7 @@ function detectFieldType(zodField: unknown): {
255
254
  * ```
256
255
  */
257
256
  export function extractFields(schema: unknown): FieldInfo[] {
258
- if (!schema || typeof schema !== 'object') return []
257
+ if (!schema || typeof schema !== "object") return []
259
258
 
260
259
  const s = schema as Record<string, unknown>
261
260
 
@@ -265,7 +264,7 @@ export function extractFields(schema: unknown): FieldInfo[] {
265
264
  let shape: Record<string, unknown> | undefined
266
265
 
267
266
  // Try schema.shape (works for both v3 and v4)
268
- if (s.shape && typeof s.shape === 'object') {
267
+ if (s.shape && typeof s.shape === "object") {
269
268
  shape = s.shape as Record<string, unknown>
270
269
  }
271
270
 
@@ -274,7 +273,7 @@ export function extractFields(schema: unknown): FieldInfo[] {
274
273
  const def = s._def as Record<string, unknown> | undefined
275
274
  if (def?.shape) {
276
275
  shape =
277
- typeof def.shape === 'function'
276
+ typeof def.shape === "function"
278
277
  ? (def.shape as () => Record<string, unknown>)()
279
278
  : (def.shape as Record<string, unknown>)
280
279
  }
@@ -284,7 +283,7 @@ export function extractFields(schema: unknown): FieldInfo[] {
284
283
  if (!shape) {
285
284
  const zod = s._zod as Record<string, unknown> | undefined
286
285
  const zodDef = zod?.def as Record<string, unknown> | undefined
287
- if (zodDef?.shape && typeof zodDef.shape === 'object') {
286
+ if (zodDef?.shape && typeof zodDef.shape === "object") {
288
287
  shape = zodDef.shape as Record<string, unknown>
289
288
  }
290
289
  }
@@ -292,8 +291,7 @@ export function extractFields(schema: unknown): FieldInfo[] {
292
291
  if (!shape) return []
293
292
 
294
293
  return Object.entries(shape).map(([name, fieldSchema]) => {
295
- const { type, optional, enumValues, referenceTo } =
296
- detectFieldType(fieldSchema)
294
+ const { type, optional, enumValues, referenceTo } = detectFieldType(fieldSchema)
297
295
  const info: FieldInfo = {
298
296
  name,
299
297
  type,
@@ -309,29 +307,27 @@ export function extractFields(schema: unknown): FieldInfo[] {
309
307
  /**
310
308
  * Generate default initial values from a schema's field types.
311
309
  */
312
- export function defaultInitialValues(
313
- fields: FieldInfo[],
314
- ): Record<string, unknown> {
310
+ export function defaultInitialValues(fields: FieldInfo[]): Record<string, unknown> {
315
311
  const values: Record<string, unknown> = {}
316
312
  for (const field of fields) {
317
313
  switch (field.type) {
318
- case 'string':
319
- values[field.name] = ''
314
+ case "string":
315
+ values[field.name] = ""
320
316
  break
321
- case 'number':
317
+ case "number":
322
318
  values[field.name] = 0
323
319
  break
324
- case 'boolean':
320
+ case "boolean":
325
321
  values[field.name] = false
326
322
  break
327
- case 'enum':
328
- values[field.name] = field.enumValues?.[0] ?? ''
323
+ case "enum":
324
+ values[field.name] = field.enumValues?.[0] ?? ""
329
325
  break
330
- case 'date':
331
- values[field.name] = ''
326
+ case "date":
327
+ values[field.name] = ""
332
328
  break
333
329
  default:
334
- values[field.name] = ''
330
+ values[field.name] = ""
335
331
  }
336
332
  }
337
333
  return values