@pyreon/feature 0.11.5 → 0.11.7

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.
@@ -1,19 +1,19 @@
1
- import type { SchemaValidateFn } from "@pyreon/form"
2
- import { useForm as _useForm } from "@pyreon/form"
3
- import type { QueryKey } from "@pyreon/query"
4
- import { useMutation as _useMutation, useQuery as _useQuery, useQueryClient } from "@pyreon/query"
5
- import { batch, signal } from "@pyreon/reactivity"
6
- import { defineStore } from "@pyreon/store"
7
- import type { ColumnDef, SortingState } from "@pyreon/table"
1
+ import type { SchemaValidateFn } from '@pyreon/form'
2
+ import { useForm as _useForm } from '@pyreon/form'
3
+ import type { QueryKey } from '@pyreon/query'
4
+ import { useMutation as _useMutation, useQuery as _useQuery, useQueryClient } from '@pyreon/query'
5
+ import { batch, signal } from '@pyreon/reactivity'
6
+ import { defineStore } from '@pyreon/store'
7
+ import type { ColumnDef, SortingState } from '@pyreon/table'
8
8
  import {
9
9
  useTable as _useTable,
10
10
  getCoreRowModel,
11
11
  getFilteredRowModel,
12
12
  getPaginationRowModel,
13
13
  getSortedRowModel,
14
- } from "@pyreon/table"
15
- import { zodSchema } from "@pyreon/validation"
16
- import { defaultInitialValues, extractFields } from "./schema"
14
+ } from '@pyreon/table'
15
+ import { zodSchema } from '@pyreon/validation'
16
+ import { defaultInitialValues, extractFields } from './schema'
17
17
  import type {
18
18
  Feature,
19
19
  FeatureConfig,
@@ -21,7 +21,7 @@ import type {
21
21
  FeatureStore,
22
22
  FeatureTableOptions,
23
23
  ListOptions,
24
- } from "./types"
24
+ } from './types'
25
25
 
26
26
  // ─── Fetch wrapper ────────────────────────────────────────────────────────────
27
27
 
@@ -30,7 +30,7 @@ function createFetcher(baseFetcher: typeof fetch = fetch) {
30
30
  const res = await baseFetcher(url, init)
31
31
 
32
32
  if (!res.ok) {
33
- let message = `${init?.method ?? "GET"} ${url} failed: ${res.status}`
33
+ let message = `${init?.method ?? 'GET'} ${url} failed: ${res.status}`
34
34
  try {
35
35
  const body = await res.json()
36
36
  if (body?.message) message = body.message
@@ -41,7 +41,7 @@ function createFetcher(baseFetcher: typeof fetch = fetch) {
41
41
  })
42
42
  }
43
43
  } catch (e) {
44
- if (e instanceof Error && "errors" in e) throw e
44
+ if (e instanceof Error && 'errors' in e) throw e
45
45
  }
46
46
  throw Object.assign(new Error(message), { status: res.status })
47
47
  }
@@ -54,7 +54,7 @@ function createFetcher(baseFetcher: typeof fetch = fetch) {
54
54
  list<T>(url: string, params?: Record<string, string | number | boolean>): Promise<T[]> {
55
55
  const query = params
56
56
  ? `?${new URLSearchParams(Object.entries(params).map(([k, v]) => [k, String(v)])).toString()}`
57
- : ""
57
+ : ''
58
58
  return request<T[]>(`${url}${query}`)
59
59
  },
60
60
  getById<T>(url: string, id: string | number): Promise<T> {
@@ -62,20 +62,20 @@ function createFetcher(baseFetcher: typeof fetch = fetch) {
62
62
  },
63
63
  create<T>(url: string, data: unknown): Promise<T> {
64
64
  return request<T>(url, {
65
- method: "POST",
66
- headers: { "Content-Type": "application/json" },
65
+ method: 'POST',
66
+ headers: { 'Content-Type': 'application/json' },
67
67
  body: JSON.stringify(data),
68
68
  })
69
69
  },
70
70
  update<T>(url: string, id: string | number, data: unknown): Promise<T> {
71
71
  return request<T>(`${url}/${id}`, {
72
- method: "PUT",
73
- headers: { "Content-Type": "application/json" },
72
+ method: 'PUT',
73
+ headers: { 'Content-Type': 'application/json' },
74
74
  body: JSON.stringify(data),
75
75
  })
76
76
  },
77
77
  delete(url: string, id: string | number): Promise<void> {
78
- return request<void>(`${url}/${id}`, { method: "DELETE" })
78
+ return request<void>(`${url}/${id}`, { method: 'DELETE' })
79
79
  },
80
80
  }
81
81
  }
@@ -90,9 +90,9 @@ function createValidator<TValues extends Record<string, unknown>>(
90
90
 
91
91
  if (
92
92
  schema &&
93
- typeof schema === "object" &&
94
- "safeParseAsync" in schema &&
95
- typeof (schema as Record<string, unknown>).safeParseAsync === "function"
93
+ typeof schema === 'object' &&
94
+ 'safeParseAsync' in schema &&
95
+ typeof (schema as Record<string, unknown>).safeParseAsync === 'function'
96
96
  ) {
97
97
  return zodSchema(schema as Parameters<typeof zodSchema>[0]) as SchemaValidateFn<TValues>
98
98
  }
@@ -104,7 +104,7 @@ function createValidator<TValues extends Record<string, unknown>>(
104
104
 
105
105
  function resolvePageValue(page: number | (() => number) | undefined): number | undefined {
106
106
  if (page === undefined) return undefined
107
- if (typeof page === "function") return page()
107
+ if (typeof page === 'function') return page()
108
108
  return page
109
109
  }
110
110
 
@@ -197,7 +197,7 @@ export function defineFeature<TValues extends Record<string, unknown>>(
197
197
  params.pageSize = pageSize
198
198
  }
199
199
 
200
- const queryKeyParts: unknown[] = [...queryKeyBase, "list", params]
200
+ const queryKeyParts: unknown[] = [...queryKeyBase, 'list', params]
201
201
 
202
202
  return {
203
203
  queryKey: queryKeyParts as QueryKey,
@@ -219,7 +219,7 @@ export function defineFeature<TValues extends Record<string, unknown>>(
219
219
 
220
220
  useSearch(searchTerm, options?: ListOptions) {
221
221
  return _useQuery(() => ({
222
- queryKey: [...queryKeyBase, "search", searchTerm()],
222
+ queryKey: [...queryKeyBase, 'search', searchTerm()],
223
223
  queryFn: () => http.list<TValues>(api, { ...options?.params, q: searchTerm() }),
224
224
  enabled: searchTerm().length > 0,
225
225
  ...(options?.staleTime != null ? { staleTime: options.staleTime } : {}),
@@ -249,7 +249,7 @@ export function defineFeature<TValues extends Record<string, unknown>>(
249
249
  await client.cancelQueries({ queryKey: [name, variables.id] })
250
250
  const previous = client.getQueryData([name, variables.id])
251
251
  client.setQueryData([name, variables.id], (old: unknown) => {
252
- if (old && typeof old === "object") {
252
+ if (old && typeof old === 'object') {
253
253
  return { ...old, ...variables.data }
254
254
  }
255
255
  return variables.data
@@ -267,7 +267,7 @@ export function defineFeature<TValues extends Record<string, unknown>>(
267
267
  })
268
268
  client.invalidateQueries({ queryKey: [name, variables.id] })
269
269
  },
270
- }) as ReturnType<Feature<TValues>["useUpdate"]>
270
+ }) as ReturnType<Feature<TValues>['useUpdate']>
271
271
  },
272
272
 
273
273
  useDelete() {
@@ -285,7 +285,7 @@ export function defineFeature<TValues extends Record<string, unknown>>(
285
285
  // ─── Form ───────────────────────────────────────────────────────
286
286
 
287
287
  useForm(options?: FeatureFormOptions<TValues>) {
288
- const mode = options?.mode ?? "create"
288
+ const mode = options?.mode ?? 'create'
289
289
  const mergedInitial = {
290
290
  ...initialValues,
291
291
  ...(options?.initialValues ?? {}),
@@ -294,11 +294,11 @@ export function defineFeature<TValues extends Record<string, unknown>>(
294
294
  const form = _useForm<TValues>({
295
295
  initialValues: mergedInitial,
296
296
  ...(validate != null ? { schema: validate } : {}),
297
- validateOn: options?.validateOn ?? "blur",
297
+ validateOn: options?.validateOn ?? 'blur',
298
298
  onSubmit: async (values) => {
299
299
  try {
300
300
  let result: unknown
301
- if (mode === "edit" && options?.id !== undefined) {
301
+ if (mode === 'edit' && options?.id !== undefined) {
302
302
  result = await http.update<TValues>(api, options.id, values)
303
303
  } else {
304
304
  result = await http.create<TValues>(api, values)
@@ -312,7 +312,7 @@ export function defineFeature<TValues extends Record<string, unknown>>(
312
312
  })
313
313
 
314
314
  // Auto-fetch in edit mode
315
- if (mode === "edit" && options?.id !== undefined) {
315
+ if (mode === 'edit' && options?.id !== undefined) {
316
316
  form.isSubmitting.set(true)
317
317
  http.getById<TValues>(api, options.id).then(
318
318
  (data) => {
@@ -349,10 +349,10 @@ export function defineFeature<TValues extends Record<string, unknown>>(
349
349
  }))
350
350
 
351
351
  const sorting = signal<SortingState>([])
352
- const globalFilter = signal("")
352
+ const globalFilter = signal('')
353
353
 
354
354
  const table = _useTable(() => ({
355
- data: typeof data === "function" ? data() : data,
355
+ data: typeof data === 'function' ? data() : data,
356
356
  columns,
357
357
  state: {
358
358
  sorting: sorting(),
@@ -360,14 +360,14 @@ export function defineFeature<TValues extends Record<string, unknown>>(
360
360
  },
361
361
  onSortingChange: (updater: unknown) => {
362
362
  sorting.set(
363
- typeof updater === "function"
363
+ typeof updater === 'function'
364
364
  ? (updater as (prev: SortingState) => SortingState)(sorting())
365
365
  : (updater as SortingState),
366
366
  )
367
367
  },
368
368
  onGlobalFilterChange: (updater: unknown) => {
369
369
  globalFilter.set(
370
- typeof updater === "function"
370
+ typeof updater === 'function'
371
371
  ? (updater as (prev: string) => string)(globalFilter())
372
372
  : (updater as string),
373
373
  )
package/src/index.ts CHANGED
@@ -1,11 +1,6 @@
1
- export { defineFeature } from "./define-feature"
2
- export type { FieldInfo, FieldType, ReferenceSchema } from "./schema"
3
- export {
4
- defaultInitialValues,
5
- extractFields,
6
- isReference,
7
- reference,
8
- } from "./schema"
1
+ export { defineFeature } from './define-feature'
2
+ export type { FieldInfo, FieldType, ReferenceSchema } from './schema'
3
+ export { defaultInitialValues, extractFields, isReference, reference } from './schema'
9
4
  export type {
10
5
  Feature,
11
6
  FeatureConfig,
@@ -15,4 +10,4 @@ export type {
15
10
  FeatureTableResult,
16
11
  InferSchemaValues,
17
12
  ListOptions,
18
- } from "./types"
13
+ } 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,15 +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
187
  const innerType = def?.innerType ?? (inner._zod as Record<string, unknown>)?.def
188
- if (innerType && typeof innerType === "object") {
188
+ if (innerType && typeof innerType === 'object') {
189
189
  inner = innerType as Record<string, unknown>
190
190
  }
191
191
  }
@@ -193,32 +193,32 @@ function detectFieldType(zodField: unknown): {
193
193
  const innerTypeName = getTypeName(inner) ?? typeName
194
194
 
195
195
  // Map Zod type names to our FieldType
196
- if (!innerTypeName) return { type: "unknown", optional }
196
+ if (!innerTypeName) return { type: 'unknown', optional }
197
197
 
198
198
  const typeMap: Record<string, FieldType> = {
199
- ZodString: "string",
200
- ZodNumber: "number",
201
- ZodBoolean: "boolean",
202
- ZodDate: "date",
203
- ZodEnum: "enum",
204
- ZodNativeEnum: "enum",
205
- ZodArray: "array",
206
- 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',
207
207
  // v4 names
208
- string: "string",
209
- number: "number",
210
- boolean: "boolean",
211
- date: "date",
212
- enum: "enum",
213
- array: "array",
214
- object: "object",
208
+ string: 'string',
209
+ number: 'number',
210
+ boolean: 'boolean',
211
+ date: 'date',
212
+ enum: 'enum',
213
+ array: 'array',
214
+ object: 'object',
215
215
  }
216
216
 
217
- const type = typeMap[innerTypeName] ?? "string"
217
+ const type = typeMap[innerTypeName] ?? 'string'
218
218
 
219
219
  // Extract enum values
220
220
  let enumValues: (string | number)[] | undefined
221
- if (type === "enum") {
221
+ if (type === 'enum') {
222
222
  const def = inner._def as Record<string, unknown> | undefined
223
223
  if (def?.values && Array.isArray(def.values)) {
224
224
  enumValues = def.values as (string | number)[]
@@ -254,7 +254,7 @@ function detectFieldType(zodField: unknown): {
254
254
  * ```
255
255
  */
256
256
  export function extractFields(schema: unknown): FieldInfo[] {
257
- if (!schema || typeof schema !== "object") return []
257
+ if (!schema || typeof schema !== 'object') return []
258
258
 
259
259
  const s = schema as Record<string, unknown>
260
260
 
@@ -264,7 +264,7 @@ export function extractFields(schema: unknown): FieldInfo[] {
264
264
  let shape: Record<string, unknown> | undefined
265
265
 
266
266
  // Try schema.shape (works for both v3 and v4)
267
- if (s.shape && typeof s.shape === "object") {
267
+ if (s.shape && typeof s.shape === 'object') {
268
268
  shape = s.shape as Record<string, unknown>
269
269
  }
270
270
 
@@ -273,7 +273,7 @@ export function extractFields(schema: unknown): FieldInfo[] {
273
273
  const def = s._def as Record<string, unknown> | undefined
274
274
  if (def?.shape) {
275
275
  shape =
276
- typeof def.shape === "function"
276
+ typeof def.shape === 'function'
277
277
  ? (def.shape as () => Record<string, unknown>)()
278
278
  : (def.shape as Record<string, unknown>)
279
279
  }
@@ -283,7 +283,7 @@ export function extractFields(schema: unknown): FieldInfo[] {
283
283
  if (!shape) {
284
284
  const zod = s._zod as Record<string, unknown> | undefined
285
285
  const zodDef = zod?.def as Record<string, unknown> | undefined
286
- if (zodDef?.shape && typeof zodDef.shape === "object") {
286
+ if (zodDef?.shape && typeof zodDef.shape === 'object') {
287
287
  shape = zodDef.shape as Record<string, unknown>
288
288
  }
289
289
  }
@@ -311,23 +311,23 @@ export function defaultInitialValues(fields: FieldInfo[]): Record<string, unknow
311
311
  const values: Record<string, unknown> = {}
312
312
  for (const field of fields) {
313
313
  switch (field.type) {
314
- case "string":
315
- values[field.name] = ""
314
+ case 'string':
315
+ values[field.name] = ''
316
316
  break
317
- case "number":
317
+ case 'number':
318
318
  values[field.name] = 0
319
319
  break
320
- case "boolean":
320
+ case 'boolean':
321
321
  values[field.name] = false
322
322
  break
323
- case "enum":
324
- values[field.name] = field.enumValues?.[0] ?? ""
323
+ case 'enum':
324
+ values[field.name] = field.enumValues?.[0] ?? ''
325
325
  break
326
- case "date":
327
- values[field.name] = ""
326
+ case 'date':
327
+ values[field.name] = ''
328
328
  break
329
329
  default:
330
- values[field.name] = ""
330
+ values[field.name] = ''
331
331
  }
332
332
  }
333
333
  return values