webspresso 0.0.6 → 0.0.8

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.
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Webspresso ORM - Type Definitions
3
+ * JSDoc types for IDE support and documentation
4
+ * @module core/orm/types
5
+ */
6
+
7
+ // ============================================================================
8
+ // Column Metadata Types
9
+ // ============================================================================
10
+
11
+ /**
12
+ * @typedef {'id'|'string'|'text'|'integer'|'bigint'|'float'|'decimal'|'boolean'|'date'|'datetime'|'timestamp'|'json'|'enum'|'uuid'} ColumnType
13
+ */
14
+
15
+ /**
16
+ * @typedef {Object} ColumnMeta
17
+ * @property {ColumnType} type - Database column type
18
+ * @property {boolean} [nullable=false] - Whether column allows NULL
19
+ * @property {boolean} [primary=false] - Whether column is primary key
20
+ * @property {boolean} [autoIncrement=false] - Whether column auto-increments
21
+ * @property {boolean} [unique=false] - Whether column has unique constraint
22
+ * @property {boolean} [index=false] - Whether column should be indexed
23
+ * @property {*} [default] - Default value for column
24
+ * @property {number} [maxLength] - Maximum length for string columns
25
+ * @property {number} [precision] - Precision for decimal columns
26
+ * @property {number} [scale] - Scale for decimal columns
27
+ * @property {string[]} [enumValues] - Allowed values for enum columns
28
+ * @property {string} [references] - Referenced table for foreign keys
29
+ * @property {string} [referenceColumn='id'] - Referenced column name
30
+ * @property {'create'|'update'} [auto] - Auto-set on create or update (for timestamps)
31
+ */
32
+
33
+ /**
34
+ * Encoded column metadata stored in Zod .describe()
35
+ * @typedef {Object} EncodedColumnMeta
36
+ * @property {string} __wdb__ - Marker to identify ORM metadata
37
+ * @property {ColumnMeta} meta - The actual column metadata
38
+ */
39
+
40
+ // ============================================================================
41
+ // Relation Types
42
+ // ============================================================================
43
+
44
+ /**
45
+ * @typedef {'belongsTo'|'hasMany'|'hasOne'} RelationType
46
+ */
47
+
48
+ /**
49
+ * @typedef {Object} RelationDefinition
50
+ * @property {RelationType} type - Type of relation
51
+ * @property {() => ModelDefinition} model - Lazy reference to related model
52
+ * @property {string} foreignKey - Foreign key column name
53
+ * @property {string} [localKey='id'] - Local key column name (for hasMany/hasOne)
54
+ */
55
+
56
+ /**
57
+ * @typedef {Object.<string, RelationDefinition>} RelationsMap
58
+ */
59
+
60
+ // ============================================================================
61
+ // Scope Types
62
+ // ============================================================================
63
+
64
+ /**
65
+ * @typedef {Object} ScopeOptions
66
+ * @property {boolean} [softDelete=false] - Enable soft delete (deleted_at column)
67
+ * @property {boolean} [timestamps=false] - Enable auto timestamps (created_at, updated_at)
68
+ * @property {string} [tenant] - Tenant column name for multi-tenancy
69
+ */
70
+
71
+ /**
72
+ * @typedef {Object} ScopeContext
73
+ * @property {*} [tenantId] - Current tenant ID for multi-tenant queries
74
+ * @property {boolean} [withTrashed=false] - Include soft-deleted records
75
+ * @property {boolean} [onlyTrashed=false] - Only soft-deleted records
76
+ */
77
+
78
+ // ============================================================================
79
+ // Model Types
80
+ // ============================================================================
81
+
82
+ /**
83
+ * @typedef {Object} ModelOptions
84
+ * @property {string} name - Model name (e.g., 'User')
85
+ * @property {string} table - Database table name (e.g., 'users')
86
+ * @property {import('zod').ZodObject} schema - Zod schema for validation
87
+ * @property {string} [primaryKey='id'] - Primary key column name
88
+ * @property {RelationsMap} [relations={}] - Relation definitions
89
+ * @property {ScopeOptions} [scopes={}] - Scope options
90
+ */
91
+
92
+ /**
93
+ * @typedef {Object} ModelDefinition
94
+ * @property {string} name - Model name
95
+ * @property {string} table - Database table name
96
+ * @property {import('zod').ZodObject} schema - Zod schema
97
+ * @property {string} primaryKey - Primary key column name
98
+ * @property {RelationsMap} relations - Relation definitions
99
+ * @property {ScopeOptions} scopes - Scope options
100
+ * @property {Map<string, ColumnMeta>} columns - Parsed column metadata
101
+ */
102
+
103
+ // ============================================================================
104
+ // Query Builder Types
105
+ // ============================================================================
106
+
107
+ /**
108
+ * @typedef {'='|'!='|'>'|'>='|'<'|'<='|'like'|'ilike'|'in'|'not in'|'is null'|'is not null'} WhereOperator
109
+ */
110
+
111
+ /**
112
+ * @typedef {Object} WhereClause
113
+ * @property {string} column - Column name
114
+ * @property {WhereOperator} operator - Comparison operator
115
+ * @property {*} value - Value to compare
116
+ * @property {'and'|'or'} [boolean='and'] - Boolean operator for chaining
117
+ */
118
+
119
+ /**
120
+ * @typedef {Object} OrderByClause
121
+ * @property {string} column - Column name
122
+ * @property {'asc'|'desc'} [direction='asc'] - Sort direction
123
+ */
124
+
125
+ /**
126
+ * @typedef {Object} QueryState
127
+ * @property {WhereClause[]} wheres - WHERE clauses
128
+ * @property {OrderByClause[]} orderBys - ORDER BY clauses
129
+ * @property {string[]} selects - SELECT columns
130
+ * @property {string[]} withs - Relations to eager load
131
+ * @property {number} [limitValue] - LIMIT value
132
+ * @property {number} [offsetValue] - OFFSET value
133
+ * @property {ScopeContext} scopeContext - Current scope context
134
+ */
135
+
136
+ // ============================================================================
137
+ // Repository Types
138
+ // ============================================================================
139
+
140
+ /**
141
+ * @typedef {Object} FindOptions
142
+ * @property {string[]} [with=[]] - Relations to eager load
143
+ * @property {string[]} [select] - Columns to select
144
+ */
145
+
146
+ /**
147
+ * @typedef {Object} PaginationOptions
148
+ * @property {number} [page=1] - Page number (1-indexed)
149
+ * @property {number} [perPage=15] - Items per page
150
+ */
151
+
152
+ /**
153
+ * @typedef {Object} PaginatedResult
154
+ * @property {Object[]} data - Result items
155
+ * @property {number} total - Total count
156
+ * @property {number} page - Current page
157
+ * @property {number} perPage - Items per page
158
+ * @property {number} totalPages - Total pages
159
+ */
160
+
161
+ // ============================================================================
162
+ // Migration Types
163
+ // ============================================================================
164
+
165
+ /**
166
+ * @typedef {Object} MigrationConfig
167
+ * @property {string} [directory='./migrations'] - Migrations directory
168
+ * @property {string} [tableName='knex_migrations'] - Migration tracking table
169
+ */
170
+
171
+ /**
172
+ * @typedef {Object} MigrationStatus
173
+ * @property {string} name - Migration filename
174
+ * @property {boolean} completed - Whether migration has run
175
+ * @property {Date} [ran_at] - When migration was run
176
+ * @property {number} [batch] - Migration batch number
177
+ */
178
+
179
+ /**
180
+ * @typedef {Object} MigrationResult
181
+ * @property {number} batch - Batch number
182
+ * @property {string[]} migrations - Migration names run
183
+ */
184
+
185
+ // ============================================================================
186
+ // Database Types
187
+ // ============================================================================
188
+
189
+ /**
190
+ * @typedef {Object} DatabaseConfig
191
+ * @property {string} client - Database client ('pg', 'mysql2', 'better-sqlite3')
192
+ * @property {string|Object} connection - Connection string or config object
193
+ * @property {MigrationConfig} [migrations] - Migration configuration
194
+ * @property {Object} [pool] - Connection pool settings
195
+ */
196
+
197
+ /**
198
+ * @typedef {Object} DatabaseInstance
199
+ * @property {import('knex').Knex} knex - Knex instance
200
+ * @property {function(ModelDefinition): Repository} createRepository - Create repository for model
201
+ * @property {function(function(TransactionContext): Promise): Promise} transaction - Run in transaction
202
+ * @property {MigrationManager} migrate - Migration manager
203
+ * @property {function(): Promise<void>} destroy - Close all connections
204
+ */
205
+
206
+ /**
207
+ * @typedef {Object} TransactionContext
208
+ * @property {import('knex').Knex.Transaction} trx - Knex transaction
209
+ * @property {function(ModelDefinition): Repository} createRepository - Create repository in transaction
210
+ */
211
+
212
+ /**
213
+ * @typedef {Object} Repository
214
+ * @property {function(number|string, FindOptions=): Promise<Object|null>} findById - Find by primary key
215
+ * @property {function(Object, FindOptions=): Promise<Object|null>} findOne - Find single record
216
+ * @property {function(FindOptions=): Promise<Object[]>} findAll - Find all records
217
+ * @property {function(Object): Promise<Object>} create - Create new record
218
+ * @property {function(number|string, Object): Promise<Object|null>} update - Update record
219
+ * @property {function(number|string): Promise<boolean>} delete - Delete record (soft if enabled)
220
+ * @property {function(number|string): Promise<boolean>} forceDelete - Hard delete record
221
+ * @property {function(): QueryBuilder} query - Get query builder
222
+ */
223
+
224
+ /**
225
+ * @typedef {Object} MigrationManager
226
+ * @property {function(): Promise<MigrationResult>} latest - Run pending migrations
227
+ * @property {function(Object=): Promise<MigrationResult>} rollback - Rollback migrations
228
+ * @property {function(): Promise<MigrationStatus[]>} status - Get migration status
229
+ * @property {function(string, Object=): Promise<string>} make - Create new migration file
230
+ */
231
+
232
+ // ============================================================================
233
+ // Exports (for type resolution)
234
+ // ============================================================================
235
+
236
+ module.exports = {};
237
+
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Webspresso ORM - Utilities
3
+ * Internal helper functions
4
+ * @module core/orm/utils
5
+ */
6
+
7
+ /**
8
+ * Pick specific keys from an object
9
+ * @param {Object} obj - Source object
10
+ * @param {string[]} keys - Keys to pick
11
+ * @returns {Object}
12
+ */
13
+ function pick(obj, keys) {
14
+ const result = {};
15
+ for (const key of keys) {
16
+ if (key in obj) {
17
+ result[key] = obj[key];
18
+ }
19
+ }
20
+ return result;
21
+ }
22
+
23
+ /**
24
+ * Omit specific keys from an object
25
+ * @param {Object} obj - Source object
26
+ * @param {string[]} keys - Keys to omit
27
+ * @returns {Object}
28
+ */
29
+ function omit(obj, keys) {
30
+ const result = {};
31
+ const omitSet = new Set(keys);
32
+ for (const key in obj) {
33
+ if (!omitSet.has(key)) {
34
+ result[key] = obj[key];
35
+ }
36
+ }
37
+ return result;
38
+ }
39
+
40
+ /**
41
+ * Format date for database
42
+ * @param {Date} date - Date to format
43
+ * @returns {string}
44
+ */
45
+ function formatDateForDb(date) {
46
+ return date.toISOString();
47
+ }
48
+
49
+ /**
50
+ * Generate timestamp for migration filename
51
+ * @returns {string} Format: YYYYMMDD_HHmmss
52
+ */
53
+ function generateMigrationTimestamp() {
54
+ const now = new Date();
55
+ const year = now.getFullYear();
56
+ const month = String(now.getMonth() + 1).padStart(2, '0');
57
+ const day = String(now.getDate()).padStart(2, '0');
58
+ const hours = String(now.getHours()).padStart(2, '0');
59
+ const minutes = String(now.getMinutes()).padStart(2, '0');
60
+ const seconds = String(now.getSeconds()).padStart(2, '0');
61
+
62
+ return `${year}${month}${day}_${hours}${minutes}${seconds}`;
63
+ }
64
+
65
+ /**
66
+ * Convert snake_case to camelCase
67
+ * @param {string} str - Snake case string
68
+ * @returns {string}
69
+ */
70
+ function snakeToCamel(str) {
71
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
72
+ }
73
+
74
+ /**
75
+ * Convert camelCase to snake_case
76
+ * @param {string} str - Camel case string
77
+ * @returns {string}
78
+ */
79
+ function camelToSnake(str) {
80
+ return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
81
+ }
82
+
83
+ /**
84
+ * Ensure a value is an array
85
+ * @param {*} value - Value to wrap
86
+ * @returns {Array}
87
+ */
88
+ function ensureArray(value) {
89
+ if (value === undefined || value === null) {
90
+ return [];
91
+ }
92
+ return Array.isArray(value) ? value : [value];
93
+ }
94
+
95
+ /**
96
+ * Deep clone an object (simple version, no circular refs)
97
+ * @param {Object} obj - Object to clone
98
+ * @returns {Object}
99
+ */
100
+ function deepClone(obj) {
101
+ if (obj === null || typeof obj !== 'object') {
102
+ return obj;
103
+ }
104
+ if (obj instanceof Date) {
105
+ return new Date(obj);
106
+ }
107
+ if (Array.isArray(obj)) {
108
+ return obj.map(deepClone);
109
+ }
110
+ const cloned = {};
111
+ for (const key in obj) {
112
+ cloned[key] = deepClone(obj[key]);
113
+ }
114
+ return cloned;
115
+ }
116
+
117
+ module.exports = {
118
+ pick,
119
+ omit,
120
+ formatDateForDb,
121
+ generateMigrationTimestamp,
122
+ snakeToCamel,
123
+ camelToSnake,
124
+ ensureArray,
125
+ deepClone,
126
+ };
127
+
package/index.js CHANGED
@@ -26,6 +26,12 @@ const {
26
26
  resetPluginManager
27
27
  } = require('./src/plugin-manager');
28
28
 
29
+ // ORM exports (lazy loaded)
30
+ const orm = require('./core/orm');
31
+
32
+ // Built-in plugins
33
+ const { schemaExplorerPlugin } = require('./plugins');
34
+
29
35
  module.exports = {
30
36
  // Main API
31
37
  createApp,
@@ -52,6 +58,12 @@ module.exports = {
52
58
  PluginManager,
53
59
  createPluginManager,
54
60
  getPluginManager,
55
- resetPluginManager
61
+ resetPluginManager,
62
+
63
+ // ORM
64
+ ...orm,
65
+
66
+ // Plugins
67
+ schemaExplorerPlugin,
56
68
  };
57
69
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webspresso",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Minimal, production-ready SSR framework for Node.js with file-based routing, Nunjucks templating, built-in i18n, and CLI tooling",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -18,7 +18,9 @@
18
18
  "express",
19
19
  "file-based-routing",
20
20
  "i18n",
21
- "framework"
21
+ "framework",
22
+ "orm",
23
+ "knex"
22
24
  ],
23
25
  "author": "cond",
24
26
  "license": "MIT",
@@ -38,19 +40,38 @@
38
40
  "express": "^4.18.2",
39
41
  "helmet": "^7.2.0",
40
42
  "inquirer": "^8.2.6",
43
+ "knex": "^3.1.0",
41
44
  "nunjucks": "^3.2.4",
42
45
  "zod": "^3.23.0"
43
46
  },
44
47
  "peerDependencies": {
45
- "dotenv": "^16.0.0"
48
+ "dotenv": "^16.0.0",
49
+ "pg": "^8.0.0",
50
+ "mysql2": "^3.0.0",
51
+ "better-sqlite3": "^9.0.0",
52
+ "@faker-js/faker": "^9.0.0"
46
53
  },
47
54
  "peerDependenciesMeta": {
48
55
  "dotenv": {
49
56
  "optional": true
57
+ },
58
+ "pg": {
59
+ "optional": true
60
+ },
61
+ "mysql2": {
62
+ "optional": true
63
+ },
64
+ "better-sqlite3": {
65
+ "optional": true
66
+ },
67
+ "@faker-js/faker": {
68
+ "optional": true
50
69
  }
51
70
  },
52
71
  "devDependencies": {
72
+ "@faker-js/faker": "^9.3.0",
53
73
  "@vitest/coverage-v8": "^1.2.0",
74
+ "better-sqlite3": "^11.0.0",
54
75
  "chokidar": "^3.5.3",
55
76
  "dotenv": "^16.3.1",
56
77
  "release-it": "^17.11.0",
@@ -450,3 +450,4 @@ module.exports = {
450
450
  };
451
451
 
452
452
 
453
+
@@ -57,3 +57,4 @@ module.exports = {
57
57
  del
58
58
  };
59
59
 
60
+