qdadm 0.28.0 → 0.29.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qdadm",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "Vue 3 framework for admin dashboards with PrimeVue",
5
5
  "author": "quazardous",
6
6
  "license": "MIT",
@@ -25,7 +25,9 @@
25
25
  "./module": "./src/module/index.js",
26
26
  "./utils": "./src/utils/index.js",
27
27
  "./styles": "./src/styles/index.scss",
28
- "./styles/breakpoints": "./src/styles/_breakpoints.scss"
28
+ "./styles/breakpoints": "./src/styles/_breakpoints.scss",
29
+ "./gen": "./src/gen/index.js",
30
+ "./gen/vite-plugin": "./src/gen/vite-plugin.js"
29
31
  },
30
32
  "files": [
31
33
  "src",
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Field Type Mapper
3
+ *
4
+ * Maps JSON Schema types and formats to UnifiedFieldSchema types.
5
+ * Extracted from Skybot and Faketual to eliminate duplication.
6
+ *
7
+ * Mapping priority:
8
+ * 1. Enum detection (schema.enum present -> 'select')
9
+ * 2. Custom mappings (user-provided)
10
+ * 3. Format-specific mappings (e.g., string + format:email -> email)
11
+ * 4. Base type mappings (e.g., string -> text)
12
+ *
13
+ * @module gen/FieldMapper
14
+ */
15
+
16
+ /**
17
+ * Base type mappings: JSON Schema type -> UnifiedFieldSchema type
18
+ *
19
+ * @type {Readonly<Record<string, import('./schema.js').UnifiedFieldType>>}
20
+ */
21
+ export const BASE_TYPE_MAPPINGS = Object.freeze({
22
+ string: 'text',
23
+ integer: 'number',
24
+ number: 'number',
25
+ boolean: 'boolean',
26
+ array: 'array',
27
+ object: 'object'
28
+ })
29
+
30
+ /**
31
+ * Format-specific mappings: JSON Schema format -> UnifiedFieldSchema type
32
+ *
33
+ * These take precedence over base type mappings when a format is specified.
34
+ *
35
+ * @type {Readonly<Record<string, import('./schema.js').UnifiedFieldType>>}
36
+ */
37
+ export const FORMAT_MAPPINGS = Object.freeze({
38
+ email: 'email',
39
+ 'date-time': 'datetime',
40
+ date: 'date',
41
+ uri: 'url',
42
+ url: 'url',
43
+ uuid: 'uuid',
44
+ password: 'text'
45
+ })
46
+
47
+ /**
48
+ * Custom mappings override structure
49
+ *
50
+ * @typedef {object} CustomMappings
51
+ * @property {Record<string, string>} [types] - Custom type mappings { jsonSchemaType: unifiedType }
52
+ * @property {Record<string, string>} [formats] - Custom format mappings { jsonSchemaFormat: unifiedType }
53
+ */
54
+
55
+ /**
56
+ * Minimal JSON Schema property definition for type detection
57
+ *
58
+ * @typedef {object} SchemaProperty
59
+ * @property {string} [type] - JSON Schema type (string, integer, number, boolean, array, object)
60
+ * @property {string} [format] - JSON Schema format (email, date-time, uuid, etc.)
61
+ * @property {Array<*>} [enum] - Enumeration values
62
+ */
63
+
64
+ /**
65
+ * Get the UnifiedFieldSchema type for a JSON Schema property
66
+ *
67
+ * @param {SchemaProperty} schema - JSON Schema property definition
68
+ * @param {CustomMappings} [customMappings] - Optional custom type/format mappings
69
+ * @returns {import('./schema.js').UnifiedFieldType | 'select'} - Unified field type
70
+ *
71
+ * @example
72
+ * // Basic type mapping
73
+ * getDefaultType({ type: 'string' }) // 'text'
74
+ * getDefaultType({ type: 'integer' }) // 'number'
75
+ *
76
+ * @example
77
+ * // Format takes precedence
78
+ * getDefaultType({ type: 'string', format: 'email' }) // 'email'
79
+ * getDefaultType({ type: 'string', format: 'uuid' }) // 'uuid'
80
+ *
81
+ * @example
82
+ * // Enum detection
83
+ * getDefaultType({ type: 'string', enum: ['a', 'b'] }) // 'select'
84
+ *
85
+ * @example
86
+ * // Custom mappings
87
+ * getDefaultType({ type: 'string', format: 'phone' }, {
88
+ * formats: { phone: 'text' }
89
+ * }) // 'text'
90
+ */
91
+ export function getDefaultType(schema, customMappings = {}) {
92
+ const { type, format } = schema || {}
93
+
94
+ // Check for enum pattern first (returns 'select')
95
+ if (Array.isArray(schema?.enum) && schema.enum.length > 0) {
96
+ return 'select'
97
+ }
98
+
99
+ // Check custom format mapping
100
+ if (format && customMappings.formats?.[format]) {
101
+ return customMappings.formats[format]
102
+ }
103
+
104
+ // Check default format mapping
105
+ if (format && FORMAT_MAPPINGS[format]) {
106
+ return FORMAT_MAPPINGS[format]
107
+ }
108
+
109
+ // Check custom type mapping
110
+ if (type && customMappings.types?.[type]) {
111
+ return customMappings.types[type]
112
+ }
113
+
114
+ // Fall back to base type mapping, default to 'text'
115
+ return BASE_TYPE_MAPPINGS[type] || 'text'
116
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Storage Profile Factory Pattern
3
+ *
4
+ * A storage profile factory is a function that creates storage instances for a given endpoint.
5
+ * This pattern enables DRY backend configuration by centralizing storage creation logic
6
+ * (API client, base URL, headers, error handling) in one place.
7
+ *
8
+ * Instead of configuring each entity's storage individually, you define a profile factory
9
+ * once and pass it to createManagers(), which calls it with each entity's endpoint.
10
+ *
11
+ * @module gen/StorageProfileFactory
12
+ */
13
+
14
+ /**
15
+ * Storage Profile Factory Options
16
+ *
17
+ * Per-entity options passed when creating storage from a profile factory.
18
+ * Allows overriding profile defaults for specific entities.
19
+ *
20
+ * @typedef {object} StorageProfileOptions
21
+ * @property {string} [entity] - Entity name (e.g., 'users', 'posts')
22
+ * @property {object} [client] - Override the profile's HTTP client for this entity
23
+ * @property {object} [headers] - Additional headers for this entity
24
+ * @property {boolean} [readOnly] - Force read-only mode (no create/update/delete)
25
+ * @property {Record<string, *>} [extensions] - Custom per-entity options
26
+ */
27
+
28
+ /**
29
+ * Storage Profile Factory
30
+ *
31
+ * A function that creates storage instances for a given endpoint.
32
+ * The factory encapsulates common storage configuration (API client, auth, base URL)
33
+ * and creates properly-configured storage adapters for each entity.
34
+ *
35
+ * @callback StorageProfileFactory
36
+ * @param {string} endpoint - Entity endpoint path (e.g., '/users', '/api/v1/posts')
37
+ * @param {StorageProfileOptions} [options] - Per-entity options to override profile defaults
38
+ * @returns {import('../entity/storage/IStorage.js').IStorage} Storage instance
39
+ *
40
+ * @example Basic factory using ApiStorage
41
+ * import axios from 'axios'
42
+ * import { ApiStorage } from 'qdadm'
43
+ *
44
+ * // Define profile factory with shared configuration
45
+ * const apiProfile = (endpoint, options = {}) => {
46
+ * return new ApiStorage({
47
+ * endpoint,
48
+ * client: axios.create({
49
+ * baseURL: 'https://api.example.com',
50
+ * headers: { 'Authorization': `Bearer ${getToken()}` }
51
+ * }),
52
+ * ...options
53
+ * })
54
+ * }
55
+ *
56
+ * // Use with createManagers (T00317)
57
+ * const managers = createManagers(schemas, {
58
+ * storageProfile: apiProfile
59
+ * })
60
+ *
61
+ * @example Factory with dynamic client resolution
62
+ * // For Vue/inject pattern - client resolved at call time
63
+ * const lazyApiProfile = (endpoint, options = {}) => {
64
+ * return new ApiStorage({
65
+ * endpoint,
66
+ * getClient: () => inject('apiClient'),
67
+ * ...options
68
+ * })
69
+ * }
70
+ *
71
+ * @example Factory for local development
72
+ * import { MemoryStorage } from 'qdadm'
73
+ *
74
+ * const memoryProfile = (endpoint, options = {}) => {
75
+ * const entity = options.entity || endpoint.replace(/^\//, '')
76
+ * return new MemoryStorage({
77
+ * initialData: mockData[entity] || []
78
+ * })
79
+ * }
80
+ *
81
+ * @example Factory with per-entity overrides
82
+ * const hybridProfile = (endpoint, options = {}) => {
83
+ * // Some entities use SDK, others use REST
84
+ * if (options.useSdk) {
85
+ * return new SdkStorage({
86
+ * sdk: options.sdk,
87
+ * collection: options.entity
88
+ * })
89
+ * }
90
+ * return new ApiStorage({
91
+ * endpoint,
92
+ * client: defaultClient
93
+ * })
94
+ * }
95
+ *
96
+ * @example Factory with environment-based switching
97
+ * const envAwareProfile = (endpoint, options = {}) => {
98
+ * if (import.meta.env.MODE === 'development') {
99
+ * return new MockApiStorage({ endpoint })
100
+ * }
101
+ * return new ApiStorage({
102
+ * endpoint,
103
+ * client: productionClient
104
+ * })
105
+ * }
106
+ */
107
+
108
+ // Type-only module - no runtime exports
109
+ // Types are available via JSDoc when importing this module
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Base Connector Interface
3
+ *
4
+ * Abstract base class for schema connectors. Connectors parse various sources
5
+ * (OpenAPI specs, manual definitions, etc.) into UnifiedEntitySchema format.
6
+ *
7
+ * Subclasses must implement the `parse(source)` method with source-specific logic.
8
+ *
9
+ * @module gen/connectors/BaseConnector
10
+ */
11
+
12
+ /**
13
+ * Connector options for customizing parsing behavior
14
+ *
15
+ * @typedef {object} ConnectorOptions
16
+ * @property {string} [name] - Connector instance name for debugging
17
+ * @property {boolean} [strict] - Throw on validation errors instead of warning (default: false)
18
+ * @property {Record<string, *>} [extensions] - Custom extension data passed to output schemas
19
+ */
20
+
21
+ /**
22
+ * Parse result with parsed schemas and any encountered issues
23
+ *
24
+ * @typedef {object} ParseResult
25
+ * @property {import('../schema.js').UnifiedEntitySchema[]} schemas - Parsed entity schemas
26
+ * @property {ParseWarning[]} warnings - Non-fatal issues encountered during parsing
27
+ */
28
+
29
+ /**
30
+ * Warning encountered during parsing
31
+ *
32
+ * @typedef {object} ParseWarning
33
+ * @property {string} path - Location in source where issue occurred
34
+ * @property {string} message - Description of the issue
35
+ * @property {string} [code] - Warning code for programmatic handling
36
+ */
37
+
38
+ /**
39
+ * Base connector class for parsing schema sources into UnifiedEntitySchema format.
40
+ *
41
+ * @abstract
42
+ * @example
43
+ * // Extending BaseConnector
44
+ * class ManualConnector extends BaseConnector {
45
+ * parse(source) {
46
+ * // Parse inline definitions
47
+ * return source.entities.map(e => this.transformEntity(e))
48
+ * }
49
+ * }
50
+ *
51
+ * @example
52
+ * // Using a connector
53
+ * const connector = new ManualConnector({ strict: true })
54
+ * const schemas = connector.parse(manualDefinitions)
55
+ */
56
+ export class BaseConnector {
57
+ /**
58
+ * Create a new connector instance
59
+ *
60
+ * @param {ConnectorOptions} [options={}] - Connector configuration options
61
+ */
62
+ constructor(options = {}) {
63
+ if (new.target === BaseConnector) {
64
+ throw new Error('BaseConnector is abstract and cannot be instantiated directly')
65
+ }
66
+
67
+ /** @type {string} */
68
+ this.name = options.name || this.constructor.name
69
+
70
+ /** @type {boolean} */
71
+ this.strict = options.strict ?? false
72
+
73
+ /** @type {Record<string, *>} */
74
+ this.extensions = options.extensions || {}
75
+ }
76
+
77
+ /**
78
+ * Parse a schema source into UnifiedEntitySchema format.
79
+ *
80
+ * This method must be implemented by subclasses to handle source-specific
81
+ * parsing logic. The source parameter format depends on the connector type:
82
+ * - ManualConnector: Object with entity/field definitions
83
+ * - OpenAPIConnector: OpenAPI 3.x specification object
84
+ *
85
+ * @abstract
86
+ * @param {*} source - Source data to parse (format depends on connector type)
87
+ * @returns {import('../schema.js').UnifiedEntitySchema[]} - Parsed entity schemas
88
+ * @throws {Error} - If parsing fails (in strict mode) or source is invalid
89
+ *
90
+ * @example
91
+ * // ManualConnector source format
92
+ * connector.parse({
93
+ * entities: [
94
+ * { name: 'users', endpoint: '/api/users', fields: { ... } }
95
+ * ]
96
+ * })
97
+ *
98
+ * @example
99
+ * // OpenAPIConnector source format
100
+ * connector.parse({
101
+ * openapi: '3.0.0',
102
+ * paths: { '/api/users': { ... } },
103
+ * components: { schemas: { ... } }
104
+ * })
105
+ */
106
+ parse(source) {
107
+ throw new Error(`${this.name}.parse() must be implemented by subclass`)
108
+ }
109
+
110
+ /**
111
+ * Parse with detailed result including warnings
112
+ *
113
+ * Alternative to `parse()` that returns warnings along with schemas.
114
+ * Useful when you want to handle non-fatal issues without strict mode.
115
+ *
116
+ * Default implementation wraps `parse()` with empty warnings array.
117
+ * Subclasses can override for more detailed reporting.
118
+ *
119
+ * @param {*} source - Source data to parse
120
+ * @returns {ParseResult} - Schemas and any warnings encountered
121
+ */
122
+ parseWithWarnings(source) {
123
+ return {
124
+ schemas: this.parse(source),
125
+ warnings: []
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Validate connector is properly configured before parsing
131
+ *
132
+ * Override in subclasses to add custom validation.
133
+ *
134
+ * @returns {boolean} - True if connector is valid
135
+ * @throws {Error} - If connector configuration is invalid
136
+ */
137
+ validate() {
138
+ return true
139
+ }
140
+ }
141
+
142
+ export default BaseConnector