tina4-nodejs 3.10.66 → 3.10.67

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/CLAUDE.md CHANGED
@@ -153,13 +153,39 @@ Database layer with auto-CRUD generation, seeding, fake data, and SQL translatio
153
153
  - `fakeData.ts` — ORM-aware fake data extending core (adds `forField()` with column-name heuristics)
154
154
  - `seeder.ts` — Database seeding (`seedTable` for raw SQL, `seedOrm` for model-based)
155
155
  - `sqlTranslation.ts` — Cross-engine SQL translator (`SQLTranslator`) and TTL query cache (`QueryCache`)
156
- - `BaseModel.select<T>(sql, params?)` Raw SQL query returning `T[]`
157
- - `BaseModel.selectOne<T>(sql, params?, include?)` Raw SQL query returning `T | null` (first match or null, with optional eager loading)
156
+ - **Instance methods:** `save()`, `delete()`, `forceDelete()`, `restore()`, `load(sql, params?, include?): boolean` (selectOne into self), `validate(): string[]`, `toDict(include?): Record`, `toObject(): Record`, `toArray(): unknown[]`, `toList(): unknown[]`, `toJson(): string`, `hasOne()`, `hasMany()`, `belongsTo()`
157
+ - **Static methods:** `find(id, include?)`, `findById(id, include?)`, `findAll(where?, params?, include?)`, `findOrFail(id)`, `create(data)`, `select(sql, params?)`, `selectOne(sql, params?, include?)`, `count(conditions?, params?)`, `withTrashed(conditions?, params?)`, `scope(name, filterSql, params?)`, `createTable()`, `query(): QueryBuilder`
158
158
  - QueryBuilder supports `toMongo()` for generating MongoDB query documents from the same fluent API
159
159
  - `getNextId(table: string, pkColumn?: string, generatorName?: string): Promise<number>` — Race-safe ID generation using atomic sequence table (`tina4_sequences`). SQLite/MySQL/MSSQL use `tina4_sequences` with atomic UPDATE+SELECT. PostgreSQL auto-creates sequences if missing. Firebird uses existing generators (unchanged).
160
160
 
161
161
  **`tina4_sequences` table** — Auto-created by `getNextId()` on first use for SQLite, MySQL, and MSSQL. Stores the current sequence value per table. Do not modify this table manually.
162
162
 
163
+ ### File Uploads
164
+
165
+ Multipart file uploads are available via `req.files` (array of UploadedFile objects):
166
+
167
+ ```typescript
168
+ // req.files[0] =>
169
+ {
170
+ fieldName: "avatar",
171
+ filename: "photo.png",
172
+ type: "image/png",
173
+ content: Buffer, // raw bytes — NOT base64
174
+ size: 102400
175
+ }
176
+ ```
177
+
178
+ ```typescript
179
+ post("/api/upload", (req, res) => {
180
+ const file = req.files?.find(f => f.fieldName === "avatar");
181
+ if (!file) return res.json({ error: "No file" }, 400);
182
+ fs.writeFileSync(`src/public/uploads/${file.filename}`, file.content);
183
+ return res.json({ ok: true });
184
+ });
185
+ ```
186
+
187
+ Max upload size: `TINA4_MAX_UPLOAD_SIZE` env var (default 10MB).
188
+
163
189
  ### @tina4/swagger (`packages/swagger/`)
164
190
  Auto-generates OpenAPI 3.0 docs.
165
191
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tina4-nodejs",
3
- "version": "3.10.66",
3
+ "version": "3.10.67",
4
4
  "type": "module",
5
5
  "description": "Tina4 for Node.js/TypeScript — 54 built-in features, zero dependencies",
6
6
  "keywords": ["tina4", "framework", "web", "api", "orm", "graphql", "websocket", "typescript"],
@@ -240,9 +240,20 @@ export class BaseModel {
240
240
  return (this as unknown as typeof BaseModel).findById.call(this, id, include) as T | null;
241
241
  }
242
242
 
243
- /** Alias for findById(). */
244
- static load<T extends BaseModel>(this: new (data?: Record<string, unknown>) => T, id: unknown, include?: string[]): T | null {
245
- return (this as unknown as typeof BaseModel).findById.call(this, id, include) as T | null;
243
+ /**
244
+ * Load a record into this instance via selectOne.
245
+ * Returns true if found and loaded, false otherwise.
246
+ */
247
+ load(sql: string, params?: unknown[], include?: string[]): boolean {
248
+ const ModelClass = this.constructor as typeof BaseModel & (new (data?: Record<string, unknown>) => BaseModel);
249
+ const result = ModelClass.selectOne(sql, params, include);
250
+ if (!result) return false;
251
+ const data = (result as any).toJSON ? (result as any).toJSON() : result;
252
+ for (const [key, value] of Object.entries(data)) {
253
+ (this as any)[key] = value;
254
+ }
255
+ (this as any)._exists = true;
256
+ return true;
246
257
  }
247
258
 
248
259
  /**