@stonecrop/stonecrop 0.13.4 → 0.13.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stonecrop/stonecrop",
3
- "version": "0.13.4",
3
+ "version": "0.13.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "author": {
@@ -34,7 +34,7 @@
34
34
  "pinia-shared-state": "^1.0.1",
35
35
  "pinia-xstate": "^3.0.0",
36
36
  "xstate": "^5.25.0",
37
- "@stonecrop/schema": "0.13.4"
37
+ "@stonecrop/schema": "0.13.6"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "pinia": "^3.0.4",
@@ -42,26 +42,22 @@
42
42
  "vue-router": "^5.0.6"
43
43
  },
44
44
  "devDependencies": {
45
- "@eslint/js": "^10.0.1",
46
45
  "@microsoft/api-documenter": "^7.30.5",
47
46
  "@rushstack/heft": "^1.2.17",
48
47
  "@vitejs/plugin-vue": "^6.0.6",
49
48
  "@vitest/coverage-istanbul": "^4.1.5",
50
49
  "@vue/test-utils": "^2.4.10",
51
- "eslint": "^10.3.0",
52
- "eslint-config-prettier": "^10.1.8",
53
- "eslint-plugin-vue": "^10.9.0",
54
- "globals": "^17.6.0",
55
50
  "jsdom": "^29.1.1",
56
- "typescript": "^6.0.3",
51
+ "oxlint": "1.67.0",
52
+ "oxlint-tsgolint": "0.23.0",
57
53
  "pinia": "^3.0.4",
58
- "typescript-eslint": "^8.59.1",
59
- "vue": "^3.5.33",
60
- "vue-router": "^5.0.6",
54
+ "typescript": "^6.0.3",
61
55
  "vite": "^7.3.2",
62
56
  "vitest": "^4.1.5",
63
- "@stonecrop/aform": "0.13.4",
64
- "@stonecrop/atable": "0.13.4",
57
+ "vue": "^3.5.33",
58
+ "vue-router": "^5.0.6",
59
+ "@stonecrop/aform": "0.13.6",
60
+ "@stonecrop/atable": "0.13.6",
65
61
  "stonecrop-rig": "0.7.0"
66
62
  },
67
63
  "description": "Schema-driven framework with XState workflows and HST state management",
@@ -75,7 +71,8 @@
75
71
  "_phase:build": "heft build && vite build && rushx docs",
76
72
  "build": "heft build && vite build && rushx docs",
77
73
  "docs": "bash ../common/scripts/run-docs.sh stonecrop",
78
- "lint": "eslint .",
74
+ "lint": "oxlint",
75
+ "lint:fix": "oxlint --fix",
79
76
  "preview": "vite preview",
80
77
  "test": "vitest run --coverage.enabled false",
81
78
  "test:watch": "vitest watch",
@@ -62,7 +62,7 @@ export function useLazyLink(doctype: Doctype, recordId: string, linkFieldname: s
62
62
  try {
63
63
  // Create function from serialized string and invoke it
64
64
  // The function receives the stonecrop instance and path as parameters
65
- // eslint-disable-next-line @typescript-eslint/no-implied-eval
65
+ // oxlint-disable-next-line @typescript-eslint/no-implied-eval
66
66
  const fn = new Function(
67
67
  'stonecrop',
68
68
  'path',
@@ -71,7 +71,6 @@ export function useLazyLink(doctype: Doctype, recordId: string, linkFieldname: s
71
71
  return (${handler})(stonecrop, path, hst)
72
72
  `
73
73
  )
74
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
75
74
  return await fn(stonecropInstance, getLinkPath(), hstStore)
76
75
  } catch (err) {
77
76
  throw new Error(`Custom handler failed: ${err instanceof Error ? err.message : String(err)}`, { cause: err })
@@ -116,12 +116,12 @@ export function useStonecrop(options?: {
116
116
  )
117
117
 
118
118
  // Operation log methods
119
- const undo = (hstStore: HSTNode): boolean => {
120
- return stonecrop.value?.getOperationLogStore().undo(hstStore) ?? false
119
+ const undo = (hstNode: HSTNode): boolean => {
120
+ return stonecrop.value?.getOperationLogStore().undo(hstNode) ?? false
121
121
  }
122
122
 
123
- const redo = (hstStore: HSTNode): boolean => {
124
- return stonecrop.value?.getOperationLogStore().redo(hstStore) ?? false
123
+ const redo = (hstNode: HSTNode): boolean => {
124
+ return stonecrop.value?.getOperationLogStore().redo(hstNode) ?? false
125
125
  }
126
126
 
127
127
  const startBatch = () => {
@@ -165,9 +165,9 @@ export function useStonecrop(options?: {
165
165
  actionName: string,
166
166
  recordIds?: string[],
167
167
  result: 'success' | 'failure' | 'pending' = 'success',
168
- error?: string
168
+ errorMessage?: string
169
169
  ): string => {
170
- return stonecrop.value?.getOperationLogStore().logAction(doctype, actionName, recordIds, result, error) ?? ''
170
+ return stonecrop.value?.getOperationLogStore().logAction(doctype, actionName, recordIds, result, errorMessage) ?? ''
171
171
  }
172
172
 
173
173
  const configure = (config: Partial<OperationLogConfig>) => {
@@ -462,12 +462,12 @@ export function useStonecrop(options?: {
462
462
  path: string,
463
463
  doctype: Doctype,
464
464
  recordId: string,
465
- options?: { includeNested?: boolean | string[] }
465
+ fetchOptions?: { includeNested?: boolean | string[] }
466
466
  ): Promise<void> => {
467
467
  if (!stonecrop.value) {
468
468
  throw new Error('Stonecrop instance not available')
469
469
  }
470
- return stonecrop.value.fetchNestedData(path, doctype, recordId, options)
470
+ return stonecrop.value.fetchNestedData(path, doctype, recordId, fetchOptions)
471
471
  }
472
472
 
473
473
  /**
@@ -613,7 +613,7 @@ function setupDeepReactivity(
613
613
  * Update nested object with dot-notation path
614
614
  */
615
615
  function updateNestedObject(obj: any, path: string[], value: any): void {
616
- let current = obj as Record<string, any>
616
+ let current: Record<string, any> = obj
617
617
 
618
618
  for (let i = 0; i < path.length - 1; i++) {
619
619
  const key = path[i]
@@ -622,7 +622,7 @@ function updateNestedObject(obj: any, path: string[], value: any): void {
622
622
  current[key] = isNaN(Number(path[i + 1])) ? {} : []
623
623
  }
624
624
 
625
- current = current[key] as Record<string, any>
625
+ current = current[key]
626
626
  }
627
627
 
628
628
  const finalKey = path[path.length - 1]
package/src/doctype.ts CHANGED
@@ -190,6 +190,7 @@ export default class Doctype {
190
190
  const states = workflow.states
191
191
  if (!states.includes(currentState)) return []
192
192
 
193
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Array.isArray(workflow.states) confirms WorkflowMeta format above
193
194
  const actions = (workflow as WorkflowMeta).actions
194
195
  if (!actions) return []
195
196
 
@@ -246,6 +247,7 @@ export default class Doctype {
246
247
  const workflow = this.workflow
247
248
  if (!workflow || !Array.isArray(workflow.states)) return undefined
248
249
 
250
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Array.isArray(workflow.states) confirms WorkflowMeta format above
249
251
  const actions = (workflow as WorkflowMeta).actions
250
252
  return actions?.[actionName]
251
253
  }
@@ -45,6 +45,7 @@ export class FieldTriggerEngine {
45
45
  enableRollback: options.enableRollback ?? true,
46
46
  errorHandler: options.errorHandler,
47
47
  }
48
+ return this
48
49
  }
49
50
 
50
51
  /**
@@ -111,6 +112,7 @@ export class FieldTriggerEngine {
111
112
 
112
113
  // Convert from different Map types to regular Map
113
114
  // Check for Immutable.js Map first (has entrySeq method)
115
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Immutable.js interop: runtime duck-type check follows
114
116
  const immutableActions = actions as ImmutableMap<string, string[]>
115
117
  if (typeof immutableActions.entrySeq === 'function') {
116
118
  // Immutable Map
@@ -125,6 +127,7 @@ export class FieldTriggerEngine {
125
127
  } else if (actions && typeof actions === 'object') {
126
128
  // Plain object
127
129
  Object.entries(actions).forEach(([key, value]) => {
130
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Object.entries value type is unknown at runtime; guarded by typeof object check above
128
131
  this.categorizeAction(key, value as string[], actionMap, transitionMap)
129
132
  })
130
133
  }
@@ -199,9 +202,10 @@ export class FieldTriggerEngine {
199
202
  snapshot = this.captureSnapshot(context)
200
203
  }
201
204
 
202
- // Execute actions sequentially
205
+ // Execute actions sequentially — each action may depend on state set by the previous; stop-on-error semantics require sequential execution
203
206
  for (const actionName of triggers) {
204
207
  try {
208
+ // oxlint-disable-next-line eslint/no-await-in-loop -- intentionally sequential; actions are order-dependent and stop on first failure
205
209
  const actionResult = await this.executeAction(actionName, context, options.timeout)
206
210
  actionResults.push(actionResult)
207
211
 
@@ -229,7 +233,6 @@ export class FieldTriggerEngine {
229
233
  this.restoreSnapshot(context, snapshot)
230
234
  rolledBack = true
231
235
  } catch (rollbackError) {
232
- // eslint-disable-next-line no-console
233
236
  console.error('[FieldTriggers] Rollback failed:', rollbackError)
234
237
  }
235
238
  }
@@ -245,7 +248,6 @@ export class FieldTriggerEngine {
245
248
  this.options.errorHandler(failedResult.error, context, failedResult.action)
246
249
  }
247
250
  } catch (handlerError) {
248
- // eslint-disable-next-line no-console
249
251
  console.error('[FieldTriggers] Error in global error handler:', handlerError)
250
252
  }
251
253
  }
@@ -283,9 +285,10 @@ export class FieldTriggerEngine {
283
285
 
284
286
  const results: TransitionExecutionResult[] = []
285
287
 
286
- // Execute transition actions sequentially
288
+ // Execute transition actions sequentially — transitions are state-machine steps; order and stop-on-error are required
287
289
  for (const actionName of transitionActions) {
288
290
  try {
291
+ // oxlint-disable-next-line eslint/no-await-in-loop -- intentionally sequential; transition actions are order-dependent and stop on first failure
289
292
  const actionResult = await this.executeTransitionAction(actionName, context, options.timeout)
290
293
  results.push(actionResult)
291
294
 
@@ -314,10 +317,10 @@ export class FieldTriggerEngine {
314
317
  try {
315
318
  // Call with FieldChangeContext (base context type)
316
319
  if (failedResult.error) {
320
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- action result carries string name; FieldAction cast required by errorHandler signature
317
321
  this.options.errorHandler(failedResult.error, context, failedResult.action as unknown as FieldAction)
318
322
  }
319
323
  } catch (handlerError) {
320
- // eslint-disable-next-line no-console
321
324
  console.error('[FieldTriggers] Error in global error handler:', handlerError)
322
325
  }
323
326
  }
@@ -365,6 +368,7 @@ export class FieldTriggerEngine {
365
368
  throw new Error(`Transition action "${actionName}" not found in registry`)
366
369
  }
367
370
 
371
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- actionFn may be FieldActionFunction set from globalActions fallback; polymorphic function type
368
372
  await this.executeWithTimeout(actionFn as FieldActionFunction, context, actionTimeout)
369
373
  const executionTime = performance.now() - startTime
370
374
 
@@ -515,7 +519,7 @@ export class FieldTriggerEngine {
515
519
  Promise.resolve(fn(context))
516
520
  .then(result => {
517
521
  clearTimeout(timeoutId)
518
- resolve(result)
522
+ return resolve(result)
519
523
  })
520
524
  .catch(error => {
521
525
  clearTimeout(timeoutId)
@@ -548,7 +552,6 @@ export class FieldTriggerEngine {
548
552
  return JSON.parse(JSON.stringify(recordData))
549
553
  } catch (error) {
550
554
  if (this.options.debug) {
551
- // eslint-disable-next-line no-console
552
555
  console.warn('[FieldTriggers] Failed to capture snapshot:', error)
553
556
  }
554
557
  return undefined
@@ -572,11 +575,10 @@ export class FieldTriggerEngine {
572
575
  context.store.set(recordPath, snapshot)
573
576
 
574
577
  if (this.options.debug) {
575
- // eslint-disable-next-line no-console
578
+ // oxlint-disable-next-line no-console
576
579
  console.log(`[FieldTriggers] Rolled back ${recordPath} to previous state`)
577
580
  }
578
581
  } catch (error) {
579
- // eslint-disable-next-line no-console
580
582
  console.error('[FieldTriggers] Failed to restore snapshot:', error)
581
583
  throw error
582
584
  }
@@ -93,7 +93,6 @@ const plugin: Plugin = {
93
93
  }
94
94
  } catch (error) {
95
95
  // Pinia not available - operation log won't work, but app should still function
96
- // eslint-disable-next-line no-console
97
96
  console.warn('Pinia not available - operation log features will be disabled:', error)
98
97
  }
99
98
 
package/src/registry.ts CHANGED
@@ -67,6 +67,7 @@ export default class Registry {
67
67
  Registry._root = this
68
68
  this.router = router
69
69
  this.getMeta = getMeta
70
+ return this
70
71
  }
71
72
 
72
73
  /**
@@ -161,11 +162,12 @@ export default class Registry {
161
162
  if ('fieldtype' in field && field.fieldtype === 'Link') {
162
163
  const link = linksByFieldname.get(field.fieldname)
163
164
  if (!link) {
164
- const doctype =
165
+ // oxlint-disable typescript/no-unsafe-type-assertion -- SchemaTypes union narrowed to FieldMeta by fieldtype === 'Link' check; options may not exist on all members
166
+ const linkDoctype =
165
167
  typeof (field as FieldMeta).options === 'string' ? ((field as FieldMeta).options as string) : undefined
168
+ // oxlint-enable typescript/no-unsafe-type-assertion
166
169
 
167
- if (doctype === undefined) {
168
- // eslint-disable-next-line no-console
170
+ if (linkDoctype === undefined) {
169
171
  console.warn(
170
172
  `[Stonecrop] Link field "${field.fieldname}" has no \`options\` or corresponding \`links\` declaration. ` +
171
173
  `AFormLink will be created without a \`doctype\` prop, so navigation will not work. ` +
@@ -182,7 +184,7 @@ export default class Registry {
182
184
  resolvedFields.push({
183
185
  ...fieldRest,
184
186
  component: fieldRest.component || 'AFormLink',
185
- ...(doctype !== undefined ? { doctype } : {}),
187
+ ...(linkDoctype !== undefined ? { doctype: linkDoctype } : {}),
186
188
  })
187
189
 
188
190
  continue
@@ -425,10 +427,11 @@ export default class Registry {
425
427
  const doctype = this.registry[doctypeSlug]
426
428
  if (!doctype?.links) return []
427
429
 
428
- return Object.entries(doctype.links).map(([fieldname, link]) => ({
429
- ...link,
430
- fieldname,
431
- }))
430
+ return Object.entries(doctype.links).map(([fieldname, link]) =>
431
+ Object.assign({}, link, {
432
+ fieldname,
433
+ })
434
+ )
432
435
  }
433
436
 
434
437
  /**
@@ -80,6 +80,7 @@ export class SchemaValidator {
80
80
  // Validate action registration
81
81
  if (this.options.validateActions && actions) {
82
82
  const actionsMap = actions instanceof Map ? actions : actions.toObject()
83
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- toObject() returns plain object; runtime-safe cast for Map<string, string[]>
83
84
  issues.push(...this.validateActionRegistration(doctype, actionsMap as Record<string, string[]>))
84
85
  }
85
86
 
@@ -130,10 +131,13 @@ export class SchemaValidator {
130
131
 
131
132
  // Validate nested schemas (recursively)
132
133
  if ('schema' in field) {
134
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 'schema' in field guard confirms property exists; SchemaTypes union narrowed at runtime
133
135
  const nestedSchema = (field as { schema: unknown }).schema
136
+ // oxlint-disable typescript/no-unsafe-type-assertion -- Immutable List or plain array; toArray() returns SchemaTypes elements
134
137
  const nestedArray = (
135
138
  Array.isArray(nestedSchema) ? nestedSchema : (nestedSchema as { toArray?: () => unknown[] }).toArray?.() || []
136
139
  ) as SchemaTypes[]
140
+ // oxlint-enable typescript/no-unsafe-type-assertion
137
141
  issues.push(...this.validateRequiredProperties(doctype, nestedArray))
138
142
  }
139
143
  }
@@ -149,6 +153,7 @@ export class SchemaValidator {
149
153
  const issues: ValidationIssue[] = []
150
154
 
151
155
  for (const field of schema) {
156
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 'fieldtype' in field guard confirms property exists; accessing unknown-typed fieldtype safely
152
157
  const fieldtype = 'fieldtype' in field ? (field as { fieldtype: unknown }).fieldtype : undefined
153
158
 
154
159
  // Check Link fields
@@ -194,10 +199,13 @@ export class SchemaValidator {
194
199
 
195
200
  // Recursively check nested schemas
196
201
  if ('schema' in field) {
202
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 'schema' in field guard confirms property exists; SchemaTypes union narrowed at runtime
197
203
  const nestedSchema = (field as { schema: unknown }).schema
204
+ // oxlint-disable typescript/no-unsafe-type-assertion -- Immutable List or plain array; toArray() returns SchemaTypes elements
198
205
  const nestedArray = (
199
206
  Array.isArray(nestedSchema) ? nestedSchema : (nestedSchema as { toArray?: () => unknown[] }).toArray?.() || []
200
207
  ) as SchemaTypes[]
208
+ // oxlint-enable typescript/no-unsafe-type-assertion
201
209
  issues.push(...this.validateLinkFields(doctype, nestedArray, registry))
202
210
  }
203
211
  }
@@ -371,6 +379,7 @@ export class SchemaValidator {
371
379
  if (typeof transition === 'string') {
372
380
  reachableStates.add(transition)
373
381
  } else if (transition && typeof transition === 'object') {
382
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 'target' in transition guard confirms property; accessing unknown-typed target safely
374
383
  const target = 'target' in transition ? (transition as { target: unknown }).target : undefined
375
384
  if (typeof target === 'string') {
376
385
  reachableStates.add(target)
@@ -425,6 +434,7 @@ export class SchemaValidator {
425
434
  // Check each action name
426
435
  for (const actionName of actionNames) {
427
436
  // Check if action is registered globally
437
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- introspecting FieldTriggerEngine private fields for validation; no public API for this check
428
438
  const engine = triggerEngine as unknown as {
429
439
  globalActions?: Map<string, unknown>
430
440
  globalTransitionActions?: Map<string, unknown>
package/src/stonecrop.ts CHANGED
@@ -56,6 +56,7 @@ export class Stonecrop {
56
56
  // Initialize HST store with auto-sync to Registry
57
57
  this.initializeHSTStore()
58
58
  this.setupRegistrySync()
59
+ return this
59
60
  }
60
61
 
61
62
  /**
@@ -207,7 +208,7 @@ export class Stonecrop {
207
208
  const slug = typeof doctype === 'string' ? doctype : doctype.slug
208
209
  this.ensureDoctypeExists(slug)
209
210
 
210
- const doctypeNode = this.hstStore.get(slug) as Record<string, any>
211
+ const doctypeNode = this.hstStore.get(slug)
211
212
  if (!doctypeNode || typeof doctypeNode !== 'object') {
212
213
  return []
213
214
  }
@@ -370,8 +371,10 @@ export class Stonecrop {
370
371
 
371
372
  // Store each record in HST
372
373
  records.forEach(record => {
373
- if (record.id) {
374
- this.addRecord(doctype, record.id as string, record)
374
+ if (typeof record.id === 'string' && record.id) {
375
+ this.addRecord(doctype, record.id, record)
376
+ } else if (typeof record.id === 'number') {
377
+ this.addRecord(doctype, String(record.id), record)
375
378
  }
376
379
  })
377
380
  }
@@ -477,6 +480,7 @@ export class Stonecrop {
477
480
  if (!meta?.workflow) return ''
478
481
 
479
482
  const record = this.getRecordById(slug, recordId)
483
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- HSTNode.get returns any; status is always string in workflow records
480
484
  const status = record?.get('status') as string | undefined
481
485
 
482
486
  // Handle both XState format and WorkflowMeta format
@@ -488,10 +492,12 @@ export class Stonecrop {
488
492
  initialState = workflow.states[0] ?? ''
489
493
  } else {
490
494
  // XState format: states is an object, use initial or first key
495
+ // oxlint-disable typescript/no-unsafe-type-assertion -- XState MachineConfig has `initial` but type union doesn't expose it; runtime-guarded by typeof check
491
496
  initialState =
492
497
  typeof (workflow as { initial?: unknown }).initial === 'string'
493
498
  ? (workflow as { initial: string }).initial
494
499
  : (Object.keys(workflow.states ?? {})[0] ?? '')
500
+ // oxlint-enable typescript/no-unsafe-type-assertion
495
501
  }
496
502
 
497
503
  return status || initialState
@@ -674,6 +680,7 @@ interface CodedError extends Error {
674
680
  }
675
681
 
676
682
  function createCodedError(message: string, code: string): CodedError {
683
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- extending Error with code property; CodedError cast is the standard pattern for typed error objects
677
684
  const error = new Error(message) as CodedError
678
685
  error.code = code
679
686
  return error
package/src/stores/hst.ts CHANGED
@@ -54,14 +54,8 @@ interface PiniaStore {
54
54
  [key: string]: any
55
55
  }
56
56
 
57
- // Interface for objects with property access
58
- interface PropertyAccessible {
59
- [key: string]: any
60
- }
61
-
62
57
  // Extend global interfaces
63
58
  declare global {
64
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
65
59
  interface Window extends RegistryGlobal {}
66
60
  const global: RegistryGlobal | undefined
67
61
  }
@@ -94,7 +88,8 @@ class HST {
94
88
  // In test environment, try different ways to access Registry
95
89
  // First, try the global Registry if it exists
96
90
  if (typeof globalThis !== 'undefined') {
97
- const globalRegistry = (globalThis as RegistryGlobal).Registry?._root
91
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Window interface extended with RegistryGlobal in declare global above; no other way to access globalThis properties
92
+ const globalRegistry = (globalThis as unknown as RegistryGlobal).Registry?._root
98
93
  if (globalRegistry) {
99
94
  return globalRegistry
100
95
  }
@@ -129,7 +124,7 @@ class HST {
129
124
  getDoctypeMeta(doctype: string) {
130
125
  const registry = this.getRegistry()
131
126
  if (registry && typeof registry === 'object' && 'registry' in registry) {
132
- return (registry as { registry: Record<string, any> }).registry[doctype]
127
+ return registry.registry[doctype]
133
128
  }
134
129
  return undefined
135
130
  }
@@ -199,7 +194,6 @@ class HSTProxy implements HSTNode {
199
194
  // Get current value for change context
200
195
  const fullPath = this.resolvePath(path)
201
196
  if (fullPath === undefined) {
202
- // eslint-disable-next-line no-console
203
197
  console.warn('HST.set: resolved path is undefined, skipping operation')
204
198
  return
205
199
  }
@@ -445,7 +439,7 @@ class HSTProxy implements HSTNode {
445
439
  }
446
440
 
447
441
  // Plain object
448
- return (obj as PropertyAccessible)[key]
442
+ return obj[key]
449
443
  }
450
444
 
451
445
  private setProperty(obj: any, key: string, value: any): void {
@@ -459,13 +453,13 @@ class HSTProxy implements HSTNode {
459
453
  if (obj.$patch) {
460
454
  obj.$patch({ [key]: value })
461
455
  } else {
462
- ;(obj as PropertyAccessible)[key] = value
456
+ obj[key] = value
463
457
  }
464
458
  return
465
459
  }
466
460
 
467
461
  // Vue reactive or plain object
468
- ;(obj as PropertyAccessible)[key] = value
462
+ obj[key] = value
469
463
  }
470
464
 
471
465
  private async triggerFieldActions(fullPath: string, beforeValue: any, afterValue: any): Promise<void> {
@@ -524,19 +518,13 @@ class HSTProxy implements HSTNode {
524
518
  // Silently handle trigger errors to not break the main flow
525
519
  // In production, you might want to log this error
526
520
  if (error instanceof Error) {
527
- // eslint-disable-next-line no-console
528
521
  console.warn('Field trigger error:', error.message)
529
522
  // Optional: emit an event or call error handler
530
523
  }
531
524
  }
532
525
  }
533
526
  private isVueReactive(obj: any): obj is VueReactive {
534
- return (
535
- obj &&
536
- typeof obj === 'object' &&
537
- '__v_isReactive' in obj &&
538
- (obj as { __v_isReactive: boolean }).__v_isReactive === true
539
- )
527
+ return obj && typeof obj === 'object' && '__v_isReactive' in obj && obj.__v_isReactive === true
540
528
  }
541
529
 
542
530
  private isPiniaStore(obj: any): obj is PiniaStore {
@@ -548,9 +536,9 @@ class HSTProxy implements HSTNode {
548
536
  return false
549
537
  }
550
538
 
551
- const hasGetMethod = 'get' in obj && typeof (obj as Record<string, unknown>).get === 'function'
552
- const hasSetMethod = 'set' in obj && typeof (obj as Record<string, unknown>).set === 'function'
553
- const hasHasMethod = 'has' in obj && typeof (obj as Record<string, unknown>).has === 'function'
539
+ const hasGetMethod = 'get' in obj && typeof obj.get === 'function'
540
+ const hasSetMethod = 'set' in obj && typeof obj.set === 'function'
541
+ const hasHasMethod = 'has' in obj && typeof obj.has === 'function'
554
542
 
555
543
  const hasImmutableMarkers =
556
544
  '__ownerID' in obj ||
@@ -565,14 +553,9 @@ class HSTProxy implements HSTNode {
565
553
 
566
554
  let constructorName: string | undefined
567
555
  try {
568
- const objWithConstructor = obj as Record<string, unknown>
569
- if (
570
- 'constructor' in objWithConstructor &&
571
- objWithConstructor.constructor &&
572
- typeof objWithConstructor.constructor === 'object' &&
573
- 'name' in objWithConstructor.constructor
574
- ) {
575
- const nameValue = (objWithConstructor.constructor as { name: unknown }).name
556
+ if ('constructor' in obj && obj.constructor && typeof obj.constructor === 'object' && 'name' in obj.constructor) {
557
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- obj.constructor.name accessed after 'name' in check; obj is any throughout this method
558
+ const nameValue = (obj.constructor as { name: unknown }).name
576
559
  constructorName = typeof nameValue === 'string' ? nameValue : undefined
577
560
  }
578
561
  } catch {